Skip to content

Commit

Permalink
Simplify Atof and try Ryū-style method even for very long inputs
Browse files Browse the repository at this point in the history
Change-Id: Id7aa4f3b524ee7fd64714fa97e73dd6cd15c60e1
  • Loading branch information
remyoudompheng committed Jan 20, 2019
1 parent efc3feb commit ed35176
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 19 deletions.
17 changes: 13 additions & 4 deletions src/strconv/atof.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,17 @@ func atof64(s string) (f float64, err error) {
}
}
// Try another fast path.
if ryuEnabled && !trunc {
b, ovf, ok := ryuFromDecimal(mantissa, exp, &float64info)
if ryuEnabled {
// Ryū-style method
b, ovf := ryuFromDecimal(mantissa, exp, &float64info)
ok := true
if trunc {
// check output for the rounded up mantissa.
b2, _ := ryuFromDecimal(mantissa+1, exp, &float64info)
if b != b2 {
ok = false
}
}
if ok {
f = math.Float64frombits(b)
if neg {
Expand All @@ -502,8 +511,8 @@ func atof64(s string) (f float64, err error) {
}
return f, err
}
}
if !ryuEnabled {
} else {
// Grisu3-style method
ext := new(extFloat)
if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok {
b, ovf := ext.floatBits(&float64info)
Expand Down
10 changes: 5 additions & 5 deletions src/strconv/extfloat2.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func ryuFixed(d *decimalSlice, mant uint64, exp int, prec int, flt *floatInfo) {
return
}

func ryuFromDecimal(mant uint64, exp int, flt *floatInfo) (fbits uint64, ovf, ok bool) {
func ryuFromDecimal(mant uint64, exp int, flt *floatInfo) (fbits uint64, ovf bool) {
// Conversion from decimal to binary floating-point
// can be achieved by reusing the same building blocks
// as the Ryū algorithm.
Expand All @@ -332,9 +332,9 @@ func ryuFromDecimal(mant uint64, exp int, flt *floatInfo) (fbits uint64, ovf, ok
var pow *extfloat128 // a representation of 10^q
switch {
case exp > 309:
return 0x7ff << 52, true, true
return 0x7ff << 52, true
case exp < -342:
return 0, false, true
return 0, false
case exp > 0:
pow = &ryuPowersOfTen[exp]
case exp == 0:
Expand Down Expand Up @@ -384,7 +384,7 @@ func ryuFromDecimal(mant uint64, exp int, flt *floatInfo) (fbits uint64, ovf, ok
e2 = flt.bias + 1
}
if extra > uint(blen) {
return 0.0, false, true
return 0.0, false
}
// Compute correct rounding.
extramask := uint64(1<<extra - 1)
Expand Down Expand Up @@ -427,7 +427,7 @@ func ryuFromDecimal(mant uint64, exp int, flt *floatInfo) (fbits uint64, ovf, ok
// Assemble bits.
fbits = di & (uint64(1)<<flt.mantbits - 1)
fbits |= uint64((e2-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits
return fbits, ovf, true
return fbits, ovf
}

func divisibleByPower5(m uint64, k int) bool {
Expand Down
18 changes: 8 additions & 10 deletions src/strconv/extfloat2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ var ryuAtofTests = []struct {

func TestRyuFromDecimal(t *testing.T) {
for _, x := range ryuAtofTests {
b, _, _ := RyuFromDecimal(x.mant, x.exp, &Float64info)
b, _ := RyuFromDecimal(x.mant, x.exp, &Float64info)
f := math.Float64frombits(b)
if f != x.expect {
t.Errorf("parsing %de%d, expect %v (%b), got %v (%b)",
Expand Down Expand Up @@ -326,10 +326,10 @@ func TestRyuAtofRandom(t *testing.T) {
exp := (i % 640) - 342 // range (-342, 298)

fold := OldAtof(mant, exp)
bnew, _, ryuOk := RyuFromDecimal(mant, exp, &Float64info)
bnew, _ := RyuFromDecimal(mant, exp, &Float64info)
fnew := math.Float64frombits(bnew)

if !ryuOk || fold != fnew {
if fold != fnew {
t.Logf("%de%d old=%v new=%v", mant, exp, fold, fnew)
ko++
} else {
Expand Down Expand Up @@ -364,15 +364,13 @@ func TestRyuAtofCoverage(t *testing.T) {
// Compute decimal mantissa, exponent.
mant, exp := ReadFloat(s)
f1, ok1 := FastAtof(mant, exp)
b2, _, ok2 := RyuFromDecimal(mant, exp, &Float64info)
b2, _ := RyuFromDecimal(mant, exp, &Float64info)
f2 := math.Float64frombits(b2)
if ok1 {
oldOk++
}
if ok2 {
ryuOk++
}
if ok1 && ok2 && f1 != f2 {
ryuOk++
if ok1 && f1 != f2 {
t.Fatalf("inconsistent results %s => %v %v", s, f1, f2)
}
}
Expand Down Expand Up @@ -740,8 +738,8 @@ func BenchmarkRyuAtof(b *testing.B) {
b.Run(fmt.Sprintf("%de%d", c.mant, c.exp), func(b *testing.B) {
var v uint64
for i := 0; i < b.N; i++ {
f, ovf, ok := RyuFromDecimal(c.mant, c.exp, &Float64info)
if ovf || !ok {
f, ovf := RyuFromDecimal(c.mant, c.exp, &Float64info)
if ovf {
b.Fatal("could not parse")
}
v = f
Expand Down

0 comments on commit ed35176

Please sign in to comment.