Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go 1.12 & 1.13 floating-point bug prevents encoding signalling NaN values #93

Closed
x448 opened this issue Jan 6, 2020 · 2 comments
Closed

Comments

@x448
Copy link
Contributor

x448 commented Jan 6, 2020

Some Go 1.12 and Go 1.13 bugs are preventing CBOR encoder from encoding sNaN values without them becoming qNaN values. This is a blocker for #91.

Works: Casting float32 to float64 directly doesn't turn sNaN into qNaN, which is good.
Go bug 1: Casting a float64 to float32 turns sNaN into qNaN.
Go bug 2: func (Value) Float in Go's reflect package turns float32 sNaN into float64 qNaN.

I wrote code to convert NaN while preserving signalling at cbor-go/float16 in FromNaN32ps. Its for float32 to float16 (IEEE 754 binary16) but the same principal applies.

Here are the reproducers for bug 1 and 2.

Bug 1: https://play.golang.org/p/kzLI9A07wRv

Go/Golang lost bit when casting float64 to float32, forced sNaN to qNaN
u32=0x7f800001, u32bis=0x7fc00001, diff=0x00400000

package main

import (
	"fmt"
	"math"
)

func main() {
	// init float32 with sNaN
	const u32 = uint32(0x7f800001) // IEEE 754 binary32 representation of sNaN
	f32 := math.Float32frombits(u32)

	// cast float32 to float64
	f64 := float64(f32)
	u64 := math.Float64bits(f64)

	// test passes
	sbit64 := uint64(0x8000000000000) // the signalling/quiet bit for IEEE 754 binary64
	if (u64 & sbit64) != 0 {
		fmt.Printf("Go/Golang lost bit when casting float32 to float64, forced sNaN to qNaN\n")
	}

	f32bis := float32(f64)
	u32bis := math.Float32bits(f32bis)
	diff := u32bis ^ u32

	// test fails
	if diff != 0 {
		fmt.Printf("Go/Golang lost bit when casting float64 to float32, forced sNaN to qNaN\n")
		fmt.Printf("u32=0x%08x, u32bis=0x%08x, diff=0x%08x\n", u32, u32bis, diff)
	}

}

Bug 2: https://play.golang.org/p/T7orv6p_C6h

Want sNaN, got qNaN

package main

import (
	"fmt"
	"math"
	"reflect"
)

func main() {
	// Create a float32 signalling Nan.
	f32 := math.Float32frombits(0x7f800001)

	// Create a reflect.Value object from f32.
	v := reflect.ValueOf(f32)

	// Get its underlying floating-point value as float64.
	f64 := v.Float()

	// Returned float64 value has quiet-bit on.
	u64 := math.Float64bits(f64)
	if (u64 & 0x8000000000000) != 0 {
		fmt.Println("Want sNaN, got qNaN")
	}
}
@x448
Copy link
Contributor Author

x448 commented Jan 7, 2020

If Go 1.15 resolves golang/go#36400 in reflect to allow detecting/retrieving unmodified sNaN values (whether in float32, float64, or myFloat32, etc.), then system requirements would need to be bumped up for this library.

Bumping library requirements to Go 1.15 isn't a good idea until 1.16 or 1.17 is released. So that's 1+ years from now unless there's a workaround.

EDIT: Never mind, Keith Randall's suggested workaround works great with Go 1.12 and makes these two bugs irrelevant.

@x448
Copy link
Contributor Author

x448 commented Jan 7, 2020

golang/go#36399 discussion mentioned there could be tests added to Go to determine if other architectures like ARMv8 or POWER8 force quiet bit = 1 when casting non-const floats to other sizes.

@x448 x448 changed the title Go 1.12 & 1.13 floating-point bug prevents encoding signalling NaN values Go 1.12 & 1.13 floating-point bug NO LONGER prevents encoding signalling NaN values Jan 8, 2020
@fxamacker fxamacker changed the title Go 1.12 & 1.13 floating-point bug NO LONGER prevents encoding signalling NaN values Go 1.12 & 1.13 floating-point bug prevents encoding signalling NaN values Jan 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants