Skip to content

Commit

Permalink
expression: unify casting real to string in tidb (tikv#16975) | tidb-…
Browse files Browse the repository at this point in the history
…test=pr/2325 (#53129) (#53207)

close #51109
  • Loading branch information
ti-chi-bot authored May 13, 2024
1 parent 7cfe1e0 commit 6b7a5ec
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 2 deletions.
36 changes: 35 additions & 1 deletion pkg/expression/builtin_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
package expression

import (
"bytes"
"fmt"
"math"
"strconv"
Expand Down Expand Up @@ -1033,6 +1034,39 @@ func (b *builtinCastRealAsStringSig) Clone() builtinFunc {
return newSig
}

func formatFloat(f float64, bitSize int) string {
// MySQL makes float to string max 6 significant digits
// MySQL makes double to string max 17 significant digits

// Unified to display behavior of TiDB. TiKV should also follow this rule.
const (
expFormatBig = 1e15
expFormatSmall = 1e-15
)

absVal := math.Abs(f)
isEFormat := false

if bitSize == 32 {
isEFormat = float32(absVal) >= float32(expFormatBig) || (float32(absVal) != 0 && float32(absVal) < float32(expFormatSmall))
} else {
isEFormat = absVal >= expFormatBig || (absVal != 0 && absVal < expFormatSmall)
}

dst := make([]byte, 0, 24)
if isEFormat {
dst = strconv.AppendFloat(dst, f, 'e', -1, bitSize)
if idx := bytes.IndexByte(dst, '+'); idx != -1 {
copy(dst[idx:], dst[idx+1:])
dst = dst[:len(dst)-1]
}
} else {
dst = strconv.AppendFloat(dst, f, 'f', -1, bitSize)
}

return string(dst)
}

func (b *builtinCastRealAsStringSig) evalString(ctx EvalContext, row chunk.Row) (res string, isNull bool, err error) {
val, isNull, err := b.args[0].EvalReal(ctx, row)
if isNull || err != nil {
Expand All @@ -1046,7 +1080,7 @@ func (b *builtinCastRealAsStringSig) evalString(ctx EvalContext, row chunk.Row)
// If we strconv.FormatFloat the value with 64bits, the result is incorrect!
bits = 32
}
res, err = types.ProduceStrWithSpecifiedTp(strconv.FormatFloat(val, 'f', -1, bits), b.tp, typeCtx(ctx), false)
res, err = types.ProduceStrWithSpecifiedTp(formatFloat(val, bits), b.tp, typeCtx(ctx), false)
if err != nil {
return res, false, err
}
Expand Down
55 changes: 55 additions & 0 deletions pkg/expression/builtin_cast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,61 @@ func TestCastFuncSig(t *testing.T) {
require.False(t, iRes.IsNull())
require.Equal(t, types.KindInt64, iRes.Kind())
require.Equal(t, int64(0), iRes.GetInt64())

// test cast real to string
zero := float32(0.00)
negZero := -zero
castFloat32ToStringCases := []struct {
v float32
expect string
}{
{negZero, "-0"},
{00000.0, "0"},
{4474.78125, "4474.7812"},
{1e15, "1e15"},
{-1e15, "-1e15"},
{9.99999e14, "999999000000000"},
{-9.99999e14, "-999999000000000"},
{1e15 - 1.0, "1e15"},
{-3.4028235e38, "-3.4028235e38"},
{3.4028235e38, "3.4028235e38"},
{1.1754944e-38, "1.1754944e-38"},
{1.000, "1"},
{-123456789123000.0, "-123456790000000"},
{1e-15, "0.000000000000001"},
{9.9999e-16, "9.9999e-16"},
{1.23456789123000e-9, "0.0000000012345679"},
}

for _, d := range castFloat32ToStringCases {
require.Equal(t, formatFloat(float64(d.v), 32), d.expect)
}

castFloat64ToStringCases := []struct {
v float64
expect string
}{
{float64(negZero), "-0"},
{00000.0, "0"},
{4474.78125, "4474.78125"},
{1e15, "1e15"},
{-1e15, "-1e15"},
{9.99999e14, "999999000000000"},
{-9.99999e14, "-999999000000000"},
{1e15 - 1.0, "999999999999999"},
{-1.7976931348623157e308, "-1.7976931348623157e308"},
{1.7976931348623157e308, "1.7976931348623157e308"},
{2.2250738585072014e-308, "2.2250738585072014e-308"},
{1.000, "1"},
{-123456789123000.0, "-123456789123000"},
{1e-15, "0.000000000000001"},
{9.9999e-16, "9.9999e-16"},
{1.23456789123000e-9, "0.00000000123456789123"},
}

for _, d := range castFloat64ToStringCases {
require.Equal(t, formatFloat(d.v, 64), d.expect)
}
}

func TestCastJSONAsDecimalSig(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/expression/builtin_cast_vec.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func (b *builtinCastRealAsStringSig) vecEvalString(ctx EvalContext, input *chunk
result.AppendNull()
continue
}
res, err = types.ProduceStrWithSpecifiedTp(strconv.FormatFloat(v, 'f', -1, bits), b.tp, tc, false)
res, err = types.ProduceStrWithSpecifiedTp(formatFloat(v, bits), b.tp, tc, false)
if err != nil {
return err
}
Expand Down

0 comments on commit 6b7a5ec

Please sign in to comment.