mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-03-11 15:34:56 +00:00
lib/decimal: speed up FromFloat for common case with integers
This commit is contained in:
parent
cfa5e279c2
commit
e76e21e4c7
2 changed files with 54 additions and 44 deletions
|
@ -265,64 +265,74 @@ 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 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
|
|
||||||
}
|
|
||||||
if f == 0 {
|
if f == 0 {
|
||||||
// Special case for 0.0 and -0.0
|
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
v, e = positiveFloatToDecimal(f)
|
if math.IsInf(f, 0) {
|
||||||
if minus {
|
return fromFloatInf(f)
|
||||||
v = -v
|
|
||||||
}
|
}
|
||||||
if v == 0 {
|
if f > 0 {
|
||||||
e = 0
|
v, e := positiveFloatToDecimal(f)
|
||||||
} else if v > vMax {
|
if v > vMax {
|
||||||
v = vMax
|
v = vMax
|
||||||
} else if v < vMin {
|
}
|
||||||
|
return v, e
|
||||||
|
}
|
||||||
|
v, e := positiveFloatToDecimal(-f)
|
||||||
|
v = -v
|
||||||
|
if v < vMin {
|
||||||
v = vMin
|
v = vMin
|
||||||
}
|
}
|
||||||
return v, e
|
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) {
|
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)
|
u := uint64(f)
|
||||||
if float64(u) == f {
|
if float64(u) != f {
|
||||||
// Fast path for integers.
|
return positiveFloatToDecimalSlow(f)
|
||||||
for u >= 1<<55 {
|
}
|
||||||
// Remove trailing garbage bits left after float64->uint64 conversion,
|
// Fast path for integers.
|
||||||
// since float64 contains only 53 significant bits.
|
if u < 1<<55 && u%10 != 0 {
|
||||||
// See https://en.wikipedia.org/wiki/Double-precision_floating-point_format
|
return int64(u), 0
|
||||||
u /= 10
|
}
|
||||||
scale++
|
return getDecimalAndScale(u)
|
||||||
}
|
}
|
||||||
if u%10 != 0 || u == 0 {
|
|
||||||
return int64(u), scale
|
func getDecimalAndScale(u uint64) (int64, int16) {
|
||||||
}
|
var scale int16
|
||||||
// Minimize v by converting trailing zeros to scale.
|
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
|
u /= 10
|
||||||
scale++
|
scale++
|
||||||
for u != 0 && u%10 == 0 {
|
}
|
||||||
u /= 10
|
if u%10 != 0 {
|
||||||
scale++
|
|
||||||
}
|
|
||||||
return int64(u), scale
|
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.
|
// 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue