From afdef4345f79d9b091a317aa94701c569391167e Mon Sep 17 00:00:00 2001 From: Jordan Lewis Date: Fri, 3 May 2019 13:12:31 -0400 Subject: [PATCH] encode: use 64-bit parsing for float32s Previously, float4 values were incorrectly parsed by this library. For example, a value of `1.2::float4` would be returned to the user as a float64 of `1.2000000476837158`. Now, float4s are parsed correctly, using the 64-bit-float string parsing facility, and return 1.2 as expected. Note that this problem only showed up if you used the returned float64 value from lib/pq directly - casting to a float32 would make the problem stay hidden - but since lib/pq returns float64, we should make an effort for that value to be correct anyway. --- conn_test.go | 8 ++++++-- encode.go | 9 ++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/conn_test.go b/conn_test.go index 0eba3e5a..5faf1786 100644 --- a/conn_test.go +++ b/conn_test.go @@ -1127,10 +1127,11 @@ func TestReadFloatPrecision(t *testing.T) { db := openTestConn(t) defer db.Close() - row := db.QueryRow("SELECT float4 '0.10000122', float8 '35.03554004971999'") + row := db.QueryRow("SELECT float4 '0.10000122', float8 '35.03554004971999', float4 '1.2'") var float4val float32 var float8val float64 - err := row.Scan(&float4val, &float8val) + var float4val2 float64 + err := row.Scan(&float4val, &float8val, &float4val2) if err != nil { t.Fatal(err) } @@ -1140,6 +1141,9 @@ func TestReadFloatPrecision(t *testing.T) { if float8val != float64(35.03554004971999) { t.Errorf("Expected float8 fidelity to be maintained; got no match") } + if float4val2 != float64(1.2) { + t.Errorf("Expected float4 fidelity into a float64 to be maintained; got no match") + } } func TestXactMultiStmt(t *testing.T) { diff --git a/encode.go b/encode.go index 3b0d365f..a6902fae 100644 --- a/encode.go +++ b/encode.go @@ -117,11 +117,10 @@ func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interfa } return i case oid.T_float4, oid.T_float8: - bits := 64 - if typ == oid.T_float4 { - bits = 32 - } - f, err := strconv.ParseFloat(string(s), bits) + // We always use 64 bit parsing, regardless of whether the input text is for + // a float4 or float8, because clients expect float64s for all float datatypes + // and returning a 32-bit parsed float64 produces lossy results. + f, err := strconv.ParseFloat(string(s), 64) if err != nil { errorf("%s", err) }