Skip to content

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

@x448

Description

@x448

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")
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions