]> Cypherpunks repositories - gostls13.git/commitdiff
strconv: extend Grisu3 algorithm to float32.
authorRémy Oudompheng <oudomphe@phare.normalesup.org>
Tue, 10 Jul 2012 05:44:23 +0000 (07:44 +0200)
committerRémy Oudompheng <oudomphe@phare.normalesup.org>
Tue, 10 Jul 2012 05:44:23 +0000 (07:44 +0200)
Also improve extfloat.Normalize to obtain a modest performance
gain in parsing, and add a shortcut path for exact integers.

benchmark                              old ns/op    new ns/op    delta
BenchmarkAtof64Decimal                        73           73   -0.54%
BenchmarkAtof64Float                          91           91   -0.54%
BenchmarkAtof64FloatExp                      198          180   -9.09%
BenchmarkAtof64Big                           307          308   +0.33%

BenchmarkAtof32Decimal                        72           72   +0.42%
BenchmarkAtof32Float                          83           83   -0.72%
BenchmarkAtof32FloatExp                      212          186  -12.26%
BenchmarkAtof32Random                        262          250   -4.58%

BenchmarkAppendFloatDecimal                  474          305  -35.65%
BenchmarkAppendFloat                         497          489   -1.61%
BenchmarkAppendFloatExp                      493          483   -2.03%
BenchmarkAppendFloatNegExp                   481          481   +0.00%
BenchmarkAppendFloatBig                      667          652   -2.25%

BenchmarkAppendFloat32Integer                338          307   -9.17%
BenchmarkAppendFloat32ExactFraction          364          439  +20.60%
BenchmarkAppendFloat32Point                 1299          490  -62.28%
BenchmarkAppendFloat32Exp                   2593          489  -81.14%
BenchmarkAppendFloat32NegExp                5116          481  -90.60%

R=rsc, r
CC=golang-dev, remy
https://golang.org/cl/6303087

src/pkg/strconv/decimal.go
src/pkg/strconv/extfloat.go
src/pkg/strconv/ftoa.go
src/pkg/strconv/ftoa_test.go

index a75071dcc46507ff0ef907244c09b53429bc1385..42601283d233193caa4e6a5a20eb7d2024908d0e 100644 (file)
@@ -79,7 +79,7 @@ func trim(a *decimal) {
 
 // Assign v to a.
 func (a *decimal) Assign(v uint64) {
-       var buf [50]byte
+       var buf [24]byte
 
        // Write reversed decimal in buf.
        n := 0
index 05e13bf96714709698057aac278fdb14f5a5d093..3b54d7b9f81bf45afced8bec593ccedff8f0b05c 100644 (file)
@@ -190,29 +190,24 @@ func (f *extFloat) Assign(x float64) {
        f.exp -= 64
 }
 
-// AssignComputeBounds sets f to the value of x and returns
+// AssignComputeBounds sets f to the floating point value
+// defined by mant, exp and precision given by flt. It returns
 // lower, upper such that any number in the closed interval
-// [lower, upper] is converted back to x.
-func (f *extFloat) AssignComputeBounds(x float64) (lower, upper extFloat) {
-       // Special cases.
-       bits := math.Float64bits(x)
-       flt := &float64info
-       neg := bits>>(flt.expbits+flt.mantbits) != 0
-       expBiased := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
-       mant := bits & (uint64(1)<<flt.mantbits - 1)
-
-       if expBiased == 0 {
-               // denormalized.
-               f.mant = mant
-               f.exp = 1 + flt.bias - int(flt.mantbits)
-       } else {
-               f.mant = mant | 1<<flt.mantbits
-               f.exp = expBiased + flt.bias - int(flt.mantbits)
-       }
+// [lower, upper] is converted back to the same floating point number.
+func (f *extFloat) AssignComputeBounds(mant uint64, exp int, neg bool, flt *floatInfo) (lower, upper extFloat) {
+       f.mant = mant
+       f.exp = exp - int(flt.mantbits)
        f.neg = neg
+       if f.exp <= 0 && mant == (mant>>uint(-f.exp))<<uint(-f.exp) {
+               // An exact integer
+               f.mant >>= uint(-f.exp)
+               f.exp = 0
+               return *f, *f
+       }
+       expBiased := exp - flt.bias
 
        upper = extFloat{mant: 2*f.mant + 1, exp: f.exp - 1, neg: f.neg}
-       if mant != 0 || expBiased == 1 {
+       if mant != 1<<flt.mantbits || expBiased == 1 {
                lower = extFloat{mant: 2*f.mant - 1, exp: f.exp - 1, neg: f.neg}
        } else {
                lower = extFloat{mant: 4*f.mant - 1, exp: f.exp - 2, neg: f.neg}
@@ -222,20 +217,38 @@ func (f *extFloat) AssignComputeBounds(x float64) (lower, upper extFloat) {
 
 // Normalize normalizes f so that the highest bit of the mantissa is
 // set, and returns the number by which the mantissa was left-shifted.
-func (f *extFloat) Normalize() uint {
-       if f.mant == 0 {
+func (f *extFloat) Normalize() (shift uint) {
+       mant, exp := f.mant, f.exp
+       if mant == 0 {
                return 0
        }
-       exp_before := f.exp
-       for f.mant < (1 << 55) {
-               f.mant <<= 8
-               f.exp -= 8
+       if mant>>(64-32) == 0 {
+               mant <<= 32
+               exp -= 32
+       }
+       if mant>>(64-16) == 0 {
+               mant <<= 16
+               exp -= 16
+       }
+       if mant>>(64-8) == 0 {
+               mant <<= 8
+               exp -= 8
        }
-       for f.mant < (1 << 63) {
-               f.mant <<= 1
-               f.exp -= 1
+       if mant>>(64-4) == 0 {
+               mant <<= 4
+               exp -= 4
        }
-       return uint(exp_before - f.exp)
+       if mant>>(64-2) == 0 {
+               mant <<= 2
+               exp -= 2
+       }
+       if mant>>(64-1) == 0 {
+               mant <<= 1
+               exp -= 1
+       }
+       shift = uint(f.exp - exp)
+       f.mant, f.exp = mant, exp
+       return
 }
 
 // Multiply sets f to the product f*g: the result is correctly rounded,
@@ -390,6 +403,12 @@ func (f *extFloat) ShortestDecimal(d *decimal, lower, upper *extFloat) bool {
                d.dp = 0
                d.neg = f.neg
        }
+       if f.exp == 0 && *lower == *f && *lower == *upper {
+               // an exact integer.
+               d.Assign(f.mant)
+               d.neg = f.neg
+               return true
+       }
        const minExp = -60
        const maxExp = -32
        upper.Normalize()
index 8eefbee79f21dca076a54779b15e45a7c4b13709..7ee5d07e0196e78a1e1e8bdf567f7261c4a5ca7f 100644 (file)
@@ -104,10 +104,10 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
        d := new(decimal)
        if shortest {
                ok := false
-               if optimize && bitSize == 64 {
+               if optimize {
                        // Try Grisu3 algorithm.
                        f := new(extFloat)
-                       lower, upper := f.AssignComputeBounds(val)
+                       lower, upper := f.AssignComputeBounds(mant, exp, neg, flt)
                        ok = f.ShortestDecimal(d, &lower, &upper)
                }
                if !ok {
index f69e3624ed1617f09283a7abae48fd2bd8cdbcf2..7b06235a40e5a04e5605c8293b0c3ba3f56d1db8 100644 (file)
@@ -203,37 +203,23 @@ func BenchmarkFormatFloatBig(b *testing.B) {
        }
 }
 
-func BenchmarkAppendFloatDecimal(b *testing.B) {
-       dst := make([]byte, 0, 30)
+func benchmarkAppendFloat(b *testing.B, f float64, fmt byte, prec, bitSize int) {
+       dst := make([]byte, 30)
        for i := 0; i < b.N; i++ {
-               AppendFloat(dst, 33909, 'g', -1, 64)
-       }
-}
-
-func BenchmarkAppendFloat(b *testing.B) {
-       dst := make([]byte, 0, 30)
-       for i := 0; i < b.N; i++ {
-               AppendFloat(dst, 339.7784, 'g', -1, 64)
-       }
-}
-
-func BenchmarkAppendFloatExp(b *testing.B) {
-       dst := make([]byte, 0, 30)
-       for i := 0; i < b.N; i++ {
-               AppendFloat(dst, -5.09e75, 'g', -1, 64)
-       }
-}
-
-func BenchmarkAppendFloatNegExp(b *testing.B) {
-       dst := make([]byte, 0, 30)
-       for i := 0; i < b.N; i++ {
-               AppendFloat(dst, -5.11e-95, 'g', -1, 64)
+               AppendFloat(dst[:0], f, fmt, prec, bitSize)
        }
 }
 
+func BenchmarkAppendFloatDecimal(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 64) }
+func BenchmarkAppendFloat(b *testing.B)        { benchmarkAppendFloat(b, 339.7784, 'g', -1, 64) }
+func BenchmarkAppendFloatExp(b *testing.B)     { benchmarkAppendFloat(b, -5.09e75, 'g', -1, 64) }
+func BenchmarkAppendFloatNegExp(b *testing.B)  { benchmarkAppendFloat(b, -5.11e-95, 'g', -1, 64) }
 func BenchmarkAppendFloatBig(b *testing.B) {
-       dst := make([]byte, 0, 30)
-       for i := 0; i < b.N; i++ {
-               AppendFloat(dst, 123456789123456789123456789, 'g', -1, 64)
-       }
+       benchmarkAppendFloat(b, 123456789123456789123456789, 'g', -1, 64)
 }
+
+func BenchmarkAppendFloat32Integer(b *testing.B)       { benchmarkAppendFloat(b, 33909, 'g', -1, 32) }
+func BenchmarkAppendFloat32ExactFraction(b *testing.B) { benchmarkAppendFloat(b, 3.375, 'g', -1, 32) }
+func BenchmarkAppendFloat32Point(b *testing.B)         { benchmarkAppendFloat(b, 339.7784, 'g', -1, 32) }
+func BenchmarkAppendFloat32Exp(b *testing.B)           { benchmarkAppendFloat(b, -5.09e25, 'g', -1, 32) }
+func BenchmarkAppendFloat32NegExp(b *testing.B)        { benchmarkAppendFloat(b, -5.11e-25, 'g', -1, 32) }