From 7485050c0f55492c0fdfc41f7564f3da37a1a308 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 6 Jan 2020 15:36:42 -0800 Subject: [PATCH] reflect: when Converting between float32s, don't lose signal NaNs When converting from float32->float64->float32, any signal NaNs get converted to quiet NaNs. Avoid that so using reflect.Value.Convert between two float32 types keeps the signal bit of NaNs. Update #36400 Change-Id: Ic4dd04c4be7189d2171d12b7e4e8f7cf2fb22bb4 Reviewed-on: https://go-review.googlesource.com/c/go/+/213497 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/reflect/all_test.go | 11 +++++++++++ src/reflect/value.go | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 00c18104eb..496d0e9241 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -4163,6 +4163,17 @@ func TestConvert(t *testing.T) { } } +func TestConvertNaNs(t *testing.T) { + const snan uint32 = 0x7f800001 + type myFloat32 float32 + x := V(myFloat32(math.Float32frombits(snan))) + y := x.Convert(TypeOf(float32(0))) + z := y.Interface().(float32) + if got := math.Float32bits(z); got != snan { + t.Errorf("signaling nan conversion got %x, want %x", got, snan) + } +} + type ComparableStruct struct { X int } diff --git a/src/reflect/value.go b/src/reflect/value.go index 0f5e083663..d8b5d11cbd 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -2541,6 +2541,14 @@ func makeFloat(f flag, v float64, t Type) Value { return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} } +// makeFloat returns a Value of type t equal to v, where t is a float32 type. +func makeFloat32(f flag, v float32, t Type) Value { + typ := t.common() + ptr := unsafe_New(typ) + *(*float32)(ptr) = v + return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} +} + // makeComplex returns a Value of type t equal to v (possibly truncated to complex64), // where t is a complex64 or complex128 type. func makeComplex(f flag, v complex128, t Type) Value { @@ -2613,6 +2621,12 @@ func cvtUintFloat(v Value, t Type) Value { // convertOp: floatXX -> floatXX func cvtFloat(v Value, t Type) Value { + if v.Type().Kind() == Float32 && t.Kind() == Float32 { + // Don't do any conversion if both types have underlying type float32. + // This avoids converting to float64 and back, which will + // convert a signaling NaN to a quiet NaN. See issue 36400. + return makeFloat32(v.flag.ro(), *(*float32)(v.ptr), t) + } return makeFloat(v.flag.ro(), v.Float(), t) } -- 2.50.0