lib/decimal: speed up FromFloat for common case with integers

This commit is contained in:
Aliaksandr Valialkin 2019-10-31 13:24:47 +02:00
parent cfa5e279c2
commit e76e21e4c7
2 changed files with 54 additions and 44 deletions

View file

@ -265,8 +265,29 @@ var (
// For instance, for f = -1.234 it returns v = -1234, e = -3. // For instance, for f = -1.234 it returns v = -1234, e = -3.
// //
// FromFloat doesn't work properly with NaN values, so don't pass them here. // FromFloat doesn't work properly with NaN values, so don't pass them here.
func FromFloat(f float64) (v int64, e int16) { func FromFloat(f float64) (int64, int16) {
if f == 0 {
return 0, 0
}
if math.IsInf(f, 0) { if math.IsInf(f, 0) {
return fromFloatInf(f)
}
if f > 0 {
v, e := positiveFloatToDecimal(f)
if v > vMax {
v = vMax
}
return v, e
}
v, e := positiveFloatToDecimal(-f)
v = -v
if v < vMin {
v = vMin
}
return v, e
}
func fromFloatInf(f float64) (int64, int16) {
// Special case for Inf // Special case for Inf
if math.IsInf(f, 1) { if math.IsInf(f, 1) {
return vInfPos, 0 return vInfPos, 0
@ -274,34 +295,21 @@ func FromFloat(f float64) (v int64, e int16) {
return vInfNeg, 0 return vInfNeg, 0
} }
minus := false func positiveFloatToDecimal(f float64) (int64, int16) {
if f < 0 { // There is no need in checking for f == 0, since it should be already checked by the caller.
f = -f u := uint64(f)
minus = true if float64(u) != f {
return positiveFloatToDecimalSlow(f)
} }
if f == 0 { // Fast path for integers.
// Special case for 0.0 and -0.0 if u < 1<<55 && u%10 != 0 {
return 0, 0 return int64(u), 0
} }
v, e = positiveFloatToDecimal(f) return getDecimalAndScale(u)
if minus {
v = -v
}
if v == 0 {
e = 0
} else if v > vMax {
v = vMax
} else if v < vMin {
v = vMin
}
return v, e
} }
func positiveFloatToDecimal(f float64) (int64, int16) { func getDecimalAndScale(u uint64) (int64, int16) {
var scale int16 var scale int16
u := uint64(f)
if float64(u) == f {
// Fast path for integers.
for u >= 1<<55 { for u >= 1<<55 {
// Remove trailing garbage bits left after float64->uint64 conversion, // Remove trailing garbage bits left after float64->uint64 conversion,
// since float64 contains only 53 significant bits. // since float64 contains only 53 significant bits.
@ -309,7 +317,7 @@ func positiveFloatToDecimal(f float64) (int64, int16) {
u /= 10 u /= 10
scale++ scale++
} }
if u%10 != 0 || u == 0 { if u%10 != 0 {
return int64(u), scale return int64(u), scale
} }
// Minimize v by converting trailing zeros to scale. // Minimize v by converting trailing zeros to scale.
@ -322,7 +330,9 @@ func positiveFloatToDecimal(f float64) (int64, int16) {
return int64(u), scale return int64(u), scale
} }
func positiveFloatToDecimalSlow(f float64) (int64, int16) {
// Slow path for floating point numbers. // Slow path for floating point numbers.
var scale int16
prec := conversionPrecision prec := conversionPrecision
if f > 1e6 || f < 1e-6 { if f > 1e6 || f < 1e-6 {
// Normalize f, so it is in the small range suitable // Normalize f, so it is in the small range suitable
@ -352,7 +362,7 @@ func positiveFloatToDecimal(f float64) (int64, int16) {
f *= 100 f *= 100
scale -= 2 scale -= 2
} }
u = uint64(f) u := uint64(f)
if u%10 != 0 { if u%10 != 0 {
return int64(u), scale return int64(u), scale
} }

View file

@ -18,7 +18,7 @@ func TestPositiveFloatToDecimal(t *testing.T) {
t.Fatalf("unexpected exponent for positiveFloatToDecimal(%f); got %d; want %d", f, exponent, exponentExpected) t.Fatalf("unexpected exponent for positiveFloatToDecimal(%f); got %d; want %d", f, exponent, exponentExpected)
} }
} }
f(0, 0, 0) f(0, 0, 1) // The exponent is 1 is OK here. See comment in positiveFloatToDecimal.
f(1, 1, 0) f(1, 1, 0)
f(30, 3, 1) f(30, 3, 1)
f(12345678900000000, 123456789, 8) f(12345678900000000, 123456789, 8)
@ -206,7 +206,7 @@ func TestAppendFloatToDecimal(t *testing.T) {
// no-op // no-op
testAppendFloatToDecimal(t, []float64{}, nil, 0) testAppendFloatToDecimal(t, []float64{}, nil, 0)
testAppendFloatToDecimal(t, []float64{0}, []int64{0}, 0) testAppendFloatToDecimal(t, []float64{0}, []int64{0}, 0)
testAppendFloatToDecimal(t, []float64{0, 1, -1, 12345678, -123456789}, []int64{0, 1, -1, 12345678, -123456789}, 0) testAppendFloatToDecimal(t, []float64{0, -0, 1, -1, 12345678, -123456789}, []int64{0, 0, 1, -1, 12345678, -123456789}, 0)
// upExp // upExp
testAppendFloatToDecimal(t, []float64{-24, 0, 4.123, 0.3}, []int64{-24000, 0, 4123, 300}, -3) testAppendFloatToDecimal(t, []float64{-24, 0, 4.123, 0.3}, []int64{-24000, 0, 4123, 300}, -3)