Skip to content

Commit

Permalink
server: use scientific notation to format float value. (pingcap#6160)
Browse files Browse the repository at this point in the history
  • Loading branch information
coocood authored and winoros committed Mar 28, 2018
1 parent 936d7b7 commit 951bc41
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 3 deletions.
30 changes: 27 additions & 3 deletions server/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
package server

import (
"bytes"
"encoding/binary"
"io"
"math"
Expand Down Expand Up @@ -293,14 +294,14 @@ func dumpTextRow(buffer []byte, columns []*ColumnInfo, row types.Row) ([]byte, e
if columns[i].Decimal > 0 && int(col.Decimal) != mysql.NotFixedDec {
prec = int(col.Decimal)
}
tmp = strconv.AppendFloat(tmp[:0], float64(row.GetFloat32(i)), 'f', prec, 32)
tmp = appendFormatFloat(tmp[:0], float64(row.GetFloat32(i)), prec, 32)
buffer = dumpLengthEncodedString(buffer, tmp)
case mysql.TypeDouble:
prec := -1
prec := types.UnspecifiedLength
if col.Decimal > 0 && int(col.Decimal) != mysql.NotFixedDec {
prec = int(col.Decimal)
}
tmp = strconv.AppendFloat(tmp[:0], row.GetFloat64(i), 'f', prec, 64)
tmp = appendFormatFloat(tmp[:0], row.GetFloat64(i), prec, 64)
buffer = dumpLengthEncodedString(buffer, tmp)
case mysql.TypeNewDecimal:
buffer = dumpLengthEncodedString(buffer, hack.Slice(row.GetMyDecimal(i).String()))
Expand All @@ -323,3 +324,26 @@ func dumpTextRow(buffer []byte, columns []*ColumnInfo, row types.Row) ([]byte, e
}
return buffer, nil
}

const (
expFormatBig = 1e15
expFormatSmall = 1e-15
)

func appendFormatFloat(in []byte, fVal float64, prec, bitSize int) []byte {
absVal := math.Abs(fVal)
var out []byte
if prec == types.UnspecifiedLength && (absVal >= expFormatBig || (absVal != 0 && absVal < expFormatSmall)) {
out = strconv.AppendFloat(in, fVal, 'e', prec, bitSize)
valStr := out[len(in):]
// remove the '+' from the string for compatibility.
plusPos := bytes.IndexByte(valStr, '+')
if plusPos > 0 {
plusPosInOut := len(in) + plusPos
out = append(out[:plusPosInOut], out[plusPosInOut+1:]...)
}
} else {
out = strconv.AppendFloat(in, fVal, 'f', prec, bitSize)
}
return out
}
97 changes: 97 additions & 0 deletions server/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,100 @@ func mustDecodeStr(c *C, b []byte) string {
c.Assert(err, IsNil)
return string(str)
}

func (s *testUtilSuite) TestAppendFormatFloat(c *C) {
tests := []struct {
fVal float64
out string
prec int
bitSize int
}{
{
99999999999999999999,
"1e20",
-1,
64,
},
{
1e15,
"1e15",
-1,
64,
},
{
9e14,
"900000000000000",
-1,
64,
},
{
-9999999999999999,
"-1e16",
-1,
64,
},
{
999999999999999,
"999999999999999",
-1,
64,
},
{
0.000000000000001,
"0.000000000000001",
-1,
64,
},
{
0.0000000000000009,
"9e-16",
-1,
64,
},
{
-0.0000000000000009,
"-9e-16",
-1,
64,
},
{
0.11111,
"0.111",
3,
64,
},
{
0.11111,
"0.111",
3,
64,
},
{
0.1111111111111111111,
"0.11111111",
-1,
32,
},
{
0.1111111111111111111,
"0.1111111111111111",
-1,
64,
},
{
0.0000000000000009,
"0.000",
3,
64,
},
{
0,
"0",
-1,
64,
},
}
for _, t := range tests {
c.Assert(string(appendFormatFloat(nil, t.fVal, t.prec, t.bitSize)), Equals, t.out)
}
}

0 comments on commit 951bc41

Please sign in to comment.