mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
lib/decimal: reduce rounding error when converting from decimal to float with negative exponent
While at it, slightly increase the conversion performance by moving fast path to the top of the loop.
This commit is contained in:
parent
c03b87dac0
commit
75eeea21ee
3 changed files with 42 additions and 6 deletions
|
@ -93,16 +93,25 @@ func AppendDecimalToFloat(dst []float64, va []int64, e int16) []float64 {
|
|||
// Extend dst capacity in order to eliminate memory allocations below.
|
||||
dst = ExtendFloat64sCapacity(dst, len(va))
|
||||
|
||||
// increase conversion precision for negative exponents by dividing by e10
|
||||
isMul := true
|
||||
if e < 0 {
|
||||
e = -e
|
||||
isMul = false
|
||||
}
|
||||
e10 := math.Pow10(int(e))
|
||||
for _, v := range va {
|
||||
// Manually inline ToFloat here for better performance
|
||||
var f float64
|
||||
f := float64(v)
|
||||
if isMul {
|
||||
f *= e10
|
||||
} else {
|
||||
f /= e10
|
||||
}
|
||||
if v == vInfPos {
|
||||
f = infPos
|
||||
} else if v == vInfNeg {
|
||||
f = infNeg
|
||||
} else {
|
||||
f = float64(v) * e10
|
||||
}
|
||||
dst = append(dst, f)
|
||||
}
|
||||
|
@ -254,13 +263,26 @@ func maxUpExponent(v int64) int16 {
|
|||
|
||||
// ToFloat returns f=v*10^e.
|
||||
func ToFloat(v int64, e int16) float64 {
|
||||
// increase conversion precision for negative exponents by dividing by e10
|
||||
isMul := true
|
||||
if e < 0 {
|
||||
e = -e
|
||||
isMul = false
|
||||
}
|
||||
e10 := math.Pow10(int(e))
|
||||
f := float64(v)
|
||||
if isMul {
|
||||
f *= e10
|
||||
} else {
|
||||
f /= e10
|
||||
}
|
||||
if v == vInfPos {
|
||||
return infPos
|
||||
}
|
||||
if v == vInfNeg {
|
||||
return infNeg
|
||||
}
|
||||
return float64(v) * math.Pow10(int(e))
|
||||
return f
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
@ -43,6 +43,9 @@ func TestPositiveFloatToDecimal(t *testing.T) {
|
|||
f(1234567890123456789e-14, 1234567890123, -8)
|
||||
f(1234567890123456789e-17, 12345678901234, -12)
|
||||
f(1234567890123456789e-20, 1234567890123, -14)
|
||||
|
||||
f(0.000874957, 874957, -9)
|
||||
f(0.001130435, 1130435, -9)
|
||||
}
|
||||
|
||||
func TestAppendDecimalToFloat(t *testing.T) {
|
||||
|
@ -52,6 +55,7 @@ func TestAppendDecimalToFloat(t *testing.T) {
|
|||
testAppendDecimalToFloat(t, []int64{0}, -10, []float64{0})
|
||||
testAppendDecimalToFloat(t, []int64{-1, -10, 0, 100}, 2, []float64{-1e2, -1e3, 0, 1e4})
|
||||
testAppendDecimalToFloat(t, []int64{-1, -10, 0, 100}, -2, []float64{-1e-2, -1e-1, 0, 1})
|
||||
testAppendDecimalToFloat(t, []int64{874957, 1130435}, -9, []float64{0.000874957, 0.001130435})
|
||||
}
|
||||
|
||||
func testAppendDecimalToFloat(t *testing.T, va []int64, e int16, fExpected []float64) {
|
||||
|
@ -323,6 +327,8 @@ func TestFloatToDecimalRoundtrip(t *testing.T) {
|
|||
f(12.34567890125)
|
||||
f(-1234567.8901256789)
|
||||
f(15e18)
|
||||
f(0.000874957)
|
||||
f(0.001130435)
|
||||
|
||||
f(math.Inf(1))
|
||||
f(math.Inf(-1))
|
||||
|
|
|
@ -8,9 +8,12 @@ import (
|
|||
)
|
||||
|
||||
func BenchmarkAppendDecimalToFloat(b *testing.B) {
|
||||
b.Run("VarNums", func(b *testing.B) {
|
||||
b.Run("RealFloat", func(b *testing.B) {
|
||||
benchmarkAppendDecimalToFloat(b, testVA)
|
||||
})
|
||||
b.Run("Integers", func(b *testing.B) {
|
||||
benchmarkAppendDecimalToFloat(b, testIntegers)
|
||||
})
|
||||
b.Run("Zeros", func(b *testing.B) {
|
||||
benchmarkAppendDecimalToFloat(b, testZeros)
|
||||
})
|
||||
|
@ -83,7 +86,7 @@ func benchmarkAppendFloatToDecimal(b *testing.B, fa []float64) {
|
|||
var testFAReal = func() []float64 {
|
||||
fa := make([]float64, 8*1024)
|
||||
for i := 0; i < len(fa); i++ {
|
||||
fa[i] = rand.NormFloat64() * 1e6
|
||||
fa[i] = rand.NormFloat64() * 1e-6
|
||||
}
|
||||
return fa
|
||||
}()
|
||||
|
@ -101,6 +104,11 @@ var testVA = func() []int64 {
|
|||
return va
|
||||
}()
|
||||
|
||||
var testIntegers = func() []int64 {
|
||||
va, _ := AppendFloatToDecimal(nil, testFAInteger)
|
||||
return va
|
||||
}()
|
||||
|
||||
func BenchmarkFromFloat(b *testing.B) {
|
||||
for _, f := range []float64{0, 1234, 12334345, 12343.4344, 123.45678901e12, 12.3454435e30} {
|
||||
b.Run(fmt.Sprintf("%g", f), func(b *testing.B) {
|
||||
|
|
Loading…
Reference in a new issue