Fix #181: float32 accuracy issue (#196)
* Fix #181: float32 accuracy issue
* Fix go fmt failure
* Add the unit test case for Issue181
* Add encFloat32 in double.go to encode float32 type
- Call encFloat32 to encode float32 while encoding
- Add unit test case to test float32 encoding
* Improve encFloat32 of double.go
* Fix git fmt failure
diff --git a/double.go b/double.go
index d70c682..376a1f4 100644
--- a/double.go
+++ b/double.go
@@ -20,6 +20,7 @@
import (
"encoding/binary"
"math"
+ "strconv"
)
import (
@@ -62,6 +63,38 @@
byte(bits>>32), byte(bits>>24), byte(bits>>16), byte(bits>>8), byte(bits))
}
+func encFloat32(b []byte, v float32) []byte {
+ fv := float32(int32(v))
+ if fv == v {
+ iv := int32(v)
+ switch iv {
+ case 0:
+ return encByte(b, BC_DOUBLE_ZERO)
+ case 1:
+ return encByte(b, BC_DOUBLE_ONE)
+ }
+ if iv >= -0x80 && iv < 0x80 {
+ return encByte(b, BC_DOUBLE_BYTE, byte(iv))
+ } else if iv >= -0x8000 && iv < 0x8000 {
+ return encByte(b, BC_DOUBLE_SHORT, byte(iv>>8), byte(iv))
+ }
+
+ goto END
+ }
+
+END:
+ if float32(int32(v*1000)) == v*1000 {
+ iv := int32(v * 1000)
+ return encByte(b, BC_DOUBLE_MILL, byte(iv>>24), byte(iv>>16), byte(iv>>8), byte(iv))
+ } else {
+ str := strconv.FormatFloat(float64(v), 'f', -1, 32)
+ d, _ := strconv.ParseFloat(str, 64)
+ bits := math.Float64bits(d)
+ return encByte(b, BC_DOUBLE, byte(bits>>56), byte(bits>>48), byte(bits>>40),
+ byte(bits>>32), byte(bits>>24), byte(bits>>16), byte(bits>>8), byte(bits))
+ }
+}
+
/////////////////////////////////////////
// Double
/////////////////////////////////////////
diff --git a/double_test.go b/double_test.go
index 111124b..43e9391 100644
--- a/double_test.go
+++ b/double_test.go
@@ -42,6 +42,33 @@
t.Logf("decode(%v) = %v, %v\n", v, res, err)
}
+func TestIssue181(t *testing.T) {
+ var (
+ v float32
+ err error
+ e *Encoder
+ d *Decoder
+ res interface{}
+ )
+
+ e = NewEncoder()
+ v = 99.8
+ e.Encode(v)
+ if len(e.Buffer()) == 0 {
+ t.Fail()
+ }
+
+ // res would be '99.800003' without patches in PR #196
+ d = NewDecoder(e.Buffer())
+ res, err = d.Decode()
+ f := res.(float64)
+ if float32(f) != v {
+ t.Errorf("decode(%v) = %v, %v\n", v, res, err)
+ return
+ }
+ t.Logf("decode(%v) = %v\n", v, res)
+}
+
func TestDouble(t *testing.T) {
testDecodeFramework(t, "replyDouble_0_0", 0.0)
testDecodeFramework(t, "replyDouble_0_001", 0.001)
diff --git a/encode.go b/encode.go
index fbeae99..ad9d5c6 100644
--- a/encode.go
+++ b/encode.go
@@ -112,7 +112,7 @@
}
case float32:
- e.buffer = encFloat(e.buffer, float64(val))
+ e.buffer = encFloat32(e.buffer, val)
case float64:
e.buffer = encFloat(e.buffer, val)