mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-23 20:37:12 +01:00
lib/decimal: speed up FromFloat for common case with integers
This commit is contained in:
parent
cfa5e279c2
commit
e76e21e4c7
@ -265,64 +265,74 @@ var (
|
||||
// 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.
|
||||
func FromFloat(f float64) (v int64, e int16) {
|
||||
if math.IsInf(f, 0) {
|
||||
// Special case for Inf
|
||||
if math.IsInf(f, 1) {
|
||||
return vInfPos, 0
|
||||
}
|
||||
return vInfNeg, 0
|
||||
}
|
||||
|
||||
minus := false
|
||||
if f < 0 {
|
||||
f = -f
|
||||
minus = true
|
||||
}
|
||||
func FromFloat(f float64) (int64, int16) {
|
||||
if f == 0 {
|
||||
// Special case for 0.0 and -0.0
|
||||
return 0, 0
|
||||
}
|
||||
v, e = positiveFloatToDecimal(f)
|
||||
if minus {
|
||||
v = -v
|
||||
if math.IsInf(f, 0) {
|
||||
return fromFloatInf(f)
|
||||
}
|
||||
if v == 0 {
|
||||
e = 0
|
||||
} else if v > vMax {
|
||||
v = vMax
|
||||
} else if v < vMin {
|
||||
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
|
||||
if math.IsInf(f, 1) {
|
||||
return vInfPos, 0
|
||||
}
|
||||
return vInfNeg, 0
|
||||
}
|
||||
|
||||
func positiveFloatToDecimal(f float64) (int64, int16) {
|
||||
var scale int16
|
||||
// There is no need in checking for f == 0, since it should be already checked by the caller.
|
||||
u := uint64(f)
|
||||
if float64(u) == f {
|
||||
// Fast path for integers.
|
||||
for u >= 1<<55 {
|
||||
// Remove trailing garbage bits left after float64->uint64 conversion,
|
||||
// since float64 contains only 53 significant bits.
|
||||
// See https://en.wikipedia.org/wiki/Double-precision_floating-point_format
|
||||
u /= 10
|
||||
scale++
|
||||
}
|
||||
if u%10 != 0 || u == 0 {
|
||||
return int64(u), scale
|
||||
}
|
||||
// Minimize v by converting trailing zeros to scale.
|
||||
if float64(u) != f {
|
||||
return positiveFloatToDecimalSlow(f)
|
||||
}
|
||||
// Fast path for integers.
|
||||
if u < 1<<55 && u%10 != 0 {
|
||||
return int64(u), 0
|
||||
}
|
||||
return getDecimalAndScale(u)
|
||||
}
|
||||
|
||||
func getDecimalAndScale(u uint64) (int64, int16) {
|
||||
var scale int16
|
||||
for u >= 1<<55 {
|
||||
// Remove trailing garbage bits left after float64->uint64 conversion,
|
||||
// since float64 contains only 53 significant bits.
|
||||
// See https://en.wikipedia.org/wiki/Double-precision_floating-point_format
|
||||
u /= 10
|
||||
scale++
|
||||
for u != 0 && u%10 == 0 {
|
||||
u /= 10
|
||||
scale++
|
||||
}
|
||||
}
|
||||
if u%10 != 0 {
|
||||
return int64(u), scale
|
||||
}
|
||||
// Minimize v by converting trailing zeros to scale.
|
||||
u /= 10
|
||||
scale++
|
||||
for u != 0 && u%10 == 0 {
|
||||
u /= 10
|
||||
scale++
|
||||
}
|
||||
return int64(u), scale
|
||||
}
|
||||
|
||||
func positiveFloatToDecimalSlow(f float64) (int64, int16) {
|
||||
// Slow path for floating point numbers.
|
||||
var scale int16
|
||||
prec := conversionPrecision
|
||||
if f > 1e6 || f < 1e-6 {
|
||||
// Normalize f, so it is in the small range suitable
|
||||
@ -352,7 +362,7 @@ func positiveFloatToDecimal(f float64) (int64, int16) {
|
||||
f *= 100
|
||||
scale -= 2
|
||||
}
|
||||
u = uint64(f)
|
||||
u := uint64(f)
|
||||
if u%10 != 0 {
|
||||
return int64(u), scale
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func TestPositiveFloatToDecimal(t *testing.T) {
|
||||
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(30, 3, 1)
|
||||
f(12345678900000000, 123456789, 8)
|
||||
@ -206,7 +206,7 @@ func TestAppendFloatToDecimal(t *testing.T) {
|
||||
// no-op
|
||||
testAppendFloatToDecimal(t, []float64{}, nil, 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
|
||||
testAppendFloatToDecimal(t, []float64{-24, 0, 4.123, 0.3}, []int64{-24000, 0, 4123, 300}, -3)
|
||||
|
Loading…
Reference in New Issue
Block a user