Skip to content

Commit

Permalink
types, json: use a more accurate way to judge whether int64/uint64 ca…
Browse files Browse the repository at this point in the history
…n be converted to float64 (#47462)

close #45686, close #46233
  • Loading branch information
YangKeao authored Oct 8, 2023
1 parent 1b9b124 commit 00d53be
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 2 deletions.
30 changes: 28 additions & 2 deletions types/json_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"encoding/json"
"fmt"
"math"
"math/bits"
"reflect"
"slices"
"strconv"
Expand Down Expand Up @@ -529,6 +530,28 @@ func (bj *BinaryJSON) UnmarshalJSON(data []byte) error {
return nil
}

func getInt64FractionLength(i int64) int {
absInt := uint64(0)
if i < 0 {
absInt = uint64(-i)
} else {
absInt = uint64(i)
}
return getUint64FractionLength(absInt)
}

func getUint64FractionLength(i uint64) int {
lz := bits.LeadingZeros64(i)
tz := bits.TrailingZeros64(i)
// 64 bit removes the leading zero, removes the trailing zero and also removes the first "1".
fraction := 64 - lz - tz - 1
if lz == 64 && tz == 64 {
fraction = 0
}

return fraction
}

// HashValue converts certain JSON values for aggregate comparisons.
// For example int64(3) == float64(3.0)
// Other than the numeric condition, this function has to construct a bidirectional map between hash value
Expand All @@ -539,15 +562,18 @@ func (bj BinaryJSON) HashValue(buf []byte) []byte {
// Convert to a FLOAT if no precision is lost.
// In the future, it will be better to convert to a DECIMAL value instead
// See: https://github.com/pingcap/tidb/issues/9988
if bj.GetInt64() == int64(float64(bj.GetInt64())) {

// A double precision float can have 52-bit in fraction part.
if getInt64FractionLength(bj.GetInt64()) <= 52 {
buf = append(buf, JSONTypeCodeFloat64)
buf = appendBinaryFloat64(buf, float64(bj.GetInt64()))
} else {
buf = append(buf, bj.TypeCode)
buf = append(buf, bj.Value...)
}
case JSONTypeCodeUint64:
if bj.GetUint64() == uint64(float64(bj.GetUint64())) {
// A double precision float can have 52-bit in fraction part.
if getUint64FractionLength(bj.GetUint64()) <= 52 {
buf = append(buf, JSONTypeCodeFloat64)
buf = appendBinaryFloat64(buf, float64(bj.GetUint64()))
} else {
Expand Down
1 change: 1 addition & 0 deletions util/codec/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,7 @@ func TestHashChunkRow(t *testing.T) {

testHashChunkRowEqual(t, types.CreateBinaryJSON(int64(1)), types.CreateBinaryJSON(float64(1.0)), true)
testHashChunkRowEqual(t, types.CreateBinaryJSON(uint64(math.MaxUint64)), types.CreateBinaryJSON(float64(math.MaxUint64)), false)
testHashChunkRowEqual(t, types.CreateBinaryJSON(int64(math.MinInt64)), types.CreateBinaryJSON(float64(math.MinInt64)), true)
}

func TestValueSizeOfSignedInt(t *testing.T) {
Expand Down

0 comments on commit 00d53be

Please sign in to comment.