Repository: amzn/ion-go
Pull request #155 (2020-09-09 21:58:18)

Handle Negative Zero and NaN Float
ion-hash-go needs these changes to get float tests passing for -0e0 and NaN.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.




Patch:
diff --git a/ion/binarywriter.go b/ion/binarywriter.go
index 900e767..99c2f2b 100644
--- a/ion/binarywriter.go
+++ b/ion/binarywriter.go
@@ -170,27 +170,30 @@ func (w *binaryWriter) writeBigInt(val *big.Int) error {
 
 // WriteFloat writes a floating-point value.
 func (w *binaryWriter) WriteFloat(val float64) error {
-	if val == 0 {
+	if val == 0 && !math.Signbit(val) {
+		// Positive zero is represented as just one byte.
 		return w.writeValue("Writer.WriteFloat", []byte{0x40})
+	} else if math.IsNaN(val) {
+		return w.writeValue("Writer.WriteFloat", []byte{0x44, 0x7F, 0xC0, 0x00, 0x00})
 	}
 
+	var bs []byte
+
 	// Can this be losslessly represented as a float32?
 	if val == float64(float32(val)) {
-		bs := make([]byte, 5)
+		bs = make([]byte, 5)
 		bs[0] = 0x44
 
 		bits := math.Float32bits(float32(val))
 		binary.BigEndian.PutUint32(bs[1:], bits)
+	} else {
+		bs = make([]byte, 9)
+		bs[0] = 0x48
 
-		return w.writeValue("Writer.WriteFloat", bs)
+		bits := math.Float64bits(val)
+		binary.BigEndian.PutUint64(bs[1:], bits)
 	}
 
-	bs := make([]byte, 9)
-	bs[0] = 0x48
-
-	bits := math.Float64bits(val)
-	binary.BigEndian.PutUint64(bs[1:], bits)
-
 	return w.writeValue("Writer.WriteFloat", bs)
 }
 


Test patch:
diff --git a/ion/binarywriter_test.go b/ion/binarywriter_test.go
index d482de1..e25721c 100644
--- a/ion/binarywriter_test.go
+++ b/ion/binarywriter_test.go
@@ -217,7 +217,7 @@ func TestWriteBinaryFloats(t *testing.T) {
 		0x48, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // -MaxFloat64
 		0x44, 0x7F, 0x80, 0x00, 0x00, // +inf (float32)
 		0x44, 0xFF, 0x80, 0x00, 0x00, // -inf (float32)
-		0x48, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // NaN
+		0x44, 0x7F, 0xC0, 0x00, 0x00, // NaN
 	}
 
 	testBinaryWriter(t, eval, func(w Writer) {
diff --git a/ion/cmp_test.go b/ion/cmp_test.go
index 84bd9de..63ae130 100644
--- a/ion/cmp_test.go
+++ b/ion/cmp_test.go
@@ -16,6 +16,7 @@
 package ion
 
 import (
+	"math"
 	"reflect"
 
 	"github.com/google/go-cmp/cmp"
@@ -32,7 +33,11 @@ type ionTimestamp struct{ Timestamp }
 type ionSymbol struct{ *SymbolToken }
 
 func (thisFloat ionFloat) eq(other ionEqual) bool {
-	return cmp.Equal(thisFloat.float64, other.(ionFloat).float64, cmpopts.EquateNaNs())
+	float1 := thisFloat.float64
+	float2 := other.(ionFloat).float64
+
+	return math.Signbit(float1) == math.Signbit(float2) &&
+		cmp.Equal(float1, float2, cmpopts.EquateNaNs())
 }
 
 func (thisDecimal ionDecimal) eq(other ionEqual) bool {
diff --git a/ion/integration_test.go b/ion/integration_test.go
index 583e043..c28fa1c 100644
--- a/ion/integration_test.go
+++ b/ion/integration_test.go
@@ -119,10 +119,7 @@ var equivsSkipList = []string{
 	"systemSymbols.ion",
 }
 
-var nonEquivsSkipList = []string{
-	"floats.ion",
-	"floatsVsDecimals.ion",
-}
+var nonEquivsSkipList = []string{}
 
 func TestLoadGood(t *testing.T) {
 	readFilesAndTest(t, goodPath, readGoodFilesSkipList, func(t *testing.T, path string) {