mirror of https://github.com/golang/go.git
strconv: extend Grisu3 algorithm to float32.
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
This commit is contained in:
parent
106dd3c93f
commit
d6147d8102
|
|
@ -79,7 +79,7 @@ func trim(a *decimal) {
|
||||||
|
|
||||||
// Assign v to a.
|
// Assign v to a.
|
||||||
func (a *decimal) Assign(v uint64) {
|
func (a *decimal) Assign(v uint64) {
|
||||||
var buf [50]byte
|
var buf [24]byte
|
||||||
|
|
||||||
// Write reversed decimal in buf.
|
// Write reversed decimal in buf.
|
||||||
n := 0
|
n := 0
|
||||||
|
|
|
||||||
|
|
@ -190,29 +190,24 @@ func (f *extFloat) Assign(x float64) {
|
||||||
f.exp -= 64
|
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 such that any number in the closed interval
|
||||||
// [lower, upper] is converted back to x.
|
// [lower, upper] is converted back to the same floating point number.
|
||||||
func (f *extFloat) AssignComputeBounds(x float64) (lower, upper extFloat) {
|
func (f *extFloat) AssignComputeBounds(mant uint64, exp int, neg bool, flt *floatInfo) (lower, upper extFloat) {
|
||||||
// Special cases.
|
f.mant = mant
|
||||||
bits := math.Float64bits(x)
|
f.exp = exp - int(flt.mantbits)
|
||||||
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)
|
|
||||||
}
|
|
||||||
f.neg = neg
|
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}
|
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}
|
lower = extFloat{mant: 2*f.mant - 1, exp: f.exp - 1, neg: f.neg}
|
||||||
} else {
|
} else {
|
||||||
lower = extFloat{mant: 4*f.mant - 1, exp: f.exp - 2, neg: f.neg}
|
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
|
// Normalize normalizes f so that the highest bit of the mantissa is
|
||||||
// set, and returns the number by which the mantissa was left-shifted.
|
// set, and returns the number by which the mantissa was left-shifted.
|
||||||
func (f *extFloat) Normalize() uint {
|
func (f *extFloat) Normalize() (shift uint) {
|
||||||
if f.mant == 0 {
|
mant, exp := f.mant, f.exp
|
||||||
|
if mant == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
exp_before := f.exp
|
if mant>>(64-32) == 0 {
|
||||||
for f.mant < (1 << 55) {
|
mant <<= 32
|
||||||
f.mant <<= 8
|
exp -= 32
|
||||||
f.exp -= 8
|
|
||||||
}
|
}
|
||||||
for f.mant < (1 << 63) {
|
if mant>>(64-16) == 0 {
|
||||||
f.mant <<= 1
|
mant <<= 16
|
||||||
f.exp -= 1
|
exp -= 16
|
||||||
}
|
}
|
||||||
return uint(exp_before - f.exp)
|
if mant>>(64-8) == 0 {
|
||||||
|
mant <<= 8
|
||||||
|
exp -= 8
|
||||||
|
}
|
||||||
|
if mant>>(64-4) == 0 {
|
||||||
|
mant <<= 4
|
||||||
|
exp -= 4
|
||||||
|
}
|
||||||
|
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,
|
// 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.dp = 0
|
||||||
d.neg = f.neg
|
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 minExp = -60
|
||||||
const maxExp = -32
|
const maxExp = -32
|
||||||
upper.Normalize()
|
upper.Normalize()
|
||||||
|
|
|
||||||
|
|
@ -104,10 +104,10 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
|
||||||
d := new(decimal)
|
d := new(decimal)
|
||||||
if shortest {
|
if shortest {
|
||||||
ok := false
|
ok := false
|
||||||
if optimize && bitSize == 64 {
|
if optimize {
|
||||||
// Try Grisu3 algorithm.
|
// Try Grisu3 algorithm.
|
||||||
f := new(extFloat)
|
f := new(extFloat)
|
||||||
lower, upper := f.AssignComputeBounds(val)
|
lower, upper := f.AssignComputeBounds(mant, exp, neg, flt)
|
||||||
ok = f.ShortestDecimal(d, &lower, &upper)
|
ok = f.ShortestDecimal(d, &lower, &upper)
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
|
|
@ -203,37 +203,23 @@ func BenchmarkFormatFloatBig(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkAppendFloatDecimal(b *testing.B) {
|
func benchmarkAppendFloat(b *testing.B, f float64, fmt byte, prec, bitSize int) {
|
||||||
dst := make([]byte, 0, 30)
|
dst := make([]byte, 30)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
AppendFloat(dst, 33909, 'g', -1, 64)
|
AppendFloat(dst[:0], f, fmt, prec, bitSize)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func BenchmarkAppendFloatBig(b *testing.B) {
|
||||||
dst := make([]byte, 0, 30)
|
benchmarkAppendFloat(b, 123456789123456789123456789, 'g', -1, 64)
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
AppendFloat(dst, 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) }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue