Skip to content

Commit 0350964

Browse files
Extend Ryū algorithm to be usable in ParseFloat.
The new function reuses the same elements to compute a floating point representation of an input decimal number. The allowed decimal mantissas are those which fit in 55 bits (16-17 decimal digits). Change-Id: I1c607f93069971b9efb7b1154d754aacd8a7b2eb
1 parent 8b9aeb9 commit 0350964

File tree

5 files changed

+341
-12
lines changed

5 files changed

+341
-12
lines changed

src/strconv/atof.go

+22-7
Original file line numberDiff line numberDiff line change
@@ -490,14 +490,29 @@ func atof64(s string) (f float64, err error) {
490490
}
491491
}
492492
// Try another fast path.
493-
ext := new(extFloat)
494-
if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok {
495-
b, ovf := ext.floatBits(&float64info)
496-
f = math.Float64frombits(b)
497-
if ovf {
498-
err = rangeError(fnParseFloat, s)
493+
if RyuEnabled && !trunc {
494+
b, ovf, ok := RyuFromDecimal(mantissa, exp, &float64info)
495+
if ok {
496+
f = math.Float64frombits(b)
497+
if neg {
498+
f = -f
499+
}
500+
if ovf {
501+
err = rangeError(fnParseFloat, s)
502+
}
503+
return f, err
504+
}
505+
}
506+
if !RyuEnabled {
507+
ext := new(extFloat)
508+
if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok {
509+
b, ovf := ext.floatBits(&float64info)
510+
f = math.Float64frombits(b)
511+
if ovf {
512+
err = rangeError(fnParseFloat, s)
513+
}
514+
return f, err
499515
}
500-
return f, err
501516
}
502517
}
503518
}

src/strconv/export_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
package strconv
66

7+
import (
8+
"math"
9+
)
10+
711
var (
812
BitSizeError = bitSizeError
913
BaseError = baseError
@@ -52,3 +56,46 @@ func ShowDecimal(d *decimalSlice) string {
5256
exp := d.dp - 1
5357
return string(d.d[0]) + "." + string(d.d[1:d.nd]) + "e" + Itoa(exp)
5458
}
59+
60+
func OldAtof(mant uint64, exp int) float64 {
61+
if f, ok := atof64exact(mant, exp, false); ok {
62+
return f
63+
}
64+
65+
// Try another fast path.
66+
ext := new(extFloat)
67+
if ok := ext.AssignDecimal(mant, exp, false, false, &float64info); ok {
68+
b, _ := ext.floatBits(&float64info)
69+
return math.Float64frombits(b)
70+
}
71+
72+
var d decimal
73+
d.Assign(mant)
74+
d.dp += exp
75+
b, _ := d.floatBits(&float64info)
76+
return math.Float64frombits(b)
77+
}
78+
79+
// FastAtof optimistically performs Atof, and only tries the float64 and Grisu fast paths.
80+
func FastAtof(mant uint64, exp int) (float64, bool) {
81+
if f, ok := atof64exact(mant, exp, false); ok {
82+
return f, true
83+
}
84+
85+
// Try another fast path.
86+
ext := new(extFloat)
87+
if ok := ext.AssignDecimal(mant, exp, false, false, &float64info); ok {
88+
b, _ := ext.floatBits(&float64info)
89+
return math.Float64frombits(b), true
90+
}
91+
92+
return 0, false
93+
}
94+
95+
func ReadFloat(s string) (mant uint64, exp int) {
96+
mantissa, exp, _, trunc, ok := readFloat(s)
97+
if !ok || trunc {
98+
panic("readFloat failure")
99+
}
100+
return mantissa, exp
101+
}

src/strconv/extfloat2.go

+146
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,128 @@ func RyuFixed(d *decimalSlice, mant uint64, exp int, prec int, flt *floatInfo) {
308308
return
309309
}
310310

311+
func RyuFromDecimal(mant uint64, exp int, flt *floatInfo) (fbits uint64, ovf, ok bool) {
312+
// Conversion from decimal to binary floating-point
313+
// can be achieved by reusing the same building blocks
314+
// as the Ryū algorithm.
315+
//
316+
// Given a decimal mantissa, we can multiply by the requested
317+
// power of ten using the same routines. The 64 bit result
318+
// is guaranteed to be correctly truncated (floored), when
319+
// the decimal mantissa fits in 55 bits.
320+
//
321+
// This covers 16-digit mantissas, and a few 17-digits values.
322+
323+
bitlen := bits.Len64(mant)
324+
if bitlen > 55 {
325+
return 0, false, false // cannot handle values that large.
326+
}
327+
// Shift mantissa to be exactly 55 bits.
328+
mant <<= uint(55 - bitlen)
329+
e2 := bitlen - 55
330+
331+
// multiply by a power of 10. It is required to know
332+
// whether the computation is exact.
333+
var pow *extfloat128 // a representation of 10^q
334+
switch {
335+
case exp > 309:
336+
return 0x7ff << 52, true, true
337+
case exp < -342:
338+
return 0, false, true
339+
case exp > 0:
340+
pow = &RyuPowersOfTen[exp]
341+
case exp == 0:
342+
// no multiply
343+
case exp < 0:
344+
pow = &RyuInvPowersOfTen[-exp]
345+
}
346+
// Is it an exact computation?
347+
exact := false
348+
switch {
349+
case exp > 55:
350+
// large positive powers of ten are not exact
351+
case 54 >= exp && exp >= 0:
352+
exact = true
353+
case 0 > exp && exp >= -25:
354+
// division by a power of ten might be exact
355+
// if mantissas are multiples of 5
356+
if divisibleByPower5(mant, -exp) {
357+
exact = true
358+
}
359+
default:
360+
// division by 10^25 cannot be exact
361+
// as 5^25 has 59 bits.
362+
}
363+
364+
// Compute Floor(x*10^q)
365+
var di uint64
366+
var d0 bool
367+
if exp == 0 {
368+
di, d0 = mant, true
369+
exact = true
370+
} else {
371+
di, d0 = ryuMultiply(mant, pow.Hi, pow.Lo)
372+
e2 += pow.Exp + 64 + 55
373+
}
374+
// If computation was an exact division, lower bits must be ignored.
375+
if exp < 0 && exact {
376+
d0 = true
377+
}
378+
// Is exponent too low? Shrink mantissa for denormals.
379+
blen := bits.Len64(di)
380+
e2 += blen - 1
381+
extra := uint(blen - 53) // number of lower bits to remove
382+
if e2 < flt.bias+1 {
383+
extra += uint(flt.bias + 1 - e2)
384+
e2 = flt.bias + 1
385+
}
386+
if extra > uint(blen) {
387+
return 0.0, false, true
388+
}
389+
// Compute correct rounding.
390+
extramask := uint64(1<<extra - 1)
391+
di, dfrac := di>>extra, di&extramask
392+
roundUp := false
393+
if exact {
394+
// If we computed an exact product, d + 1/2
395+
// should round to d+1 if 'd' is odd.
396+
roundUp = dfrac > 1<<(extra-1) ||
397+
(dfrac == 1<<(extra-1) && !d0) ||
398+
(dfrac == 1<<(extra-1) && d0 && di&1 == 1)
399+
} else {
400+
// otherwise, d+1/2 always rounds up because
401+
// we truncated below.
402+
roundUp = dfrac>>(extra-1) == 1
403+
}
404+
if dfrac != 0 {
405+
d0 = false
406+
}
407+
if roundUp {
408+
di++
409+
}
410+
411+
// Rounding might have added a bit; shift down.
412+
if di == 2<<flt.mantbits {
413+
di >>= 1
414+
e2++
415+
}
416+
417+
// Infinities.
418+
if e2-flt.bias >= 1<<flt.expbits-1 {
419+
// ±Inf
420+
di = 0
421+
e2 = 1<<flt.expbits - 1 + flt.bias
422+
ovf = true
423+
} else if di&(1<<flt.mantbits) == 0 {
424+
// Denormalized?
425+
e2 = flt.bias
426+
}
427+
// Assemble bits.
428+
fbits = di & (uint64(1)<<flt.mantbits - 1)
429+
fbits |= uint64((e2-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits
430+
return fbits, ovf, true
431+
}
432+
311433
func divisibleByPower5(m uint64, k int) bool {
312434
for i := 0; i < k; i++ {
313435
a, b := m/5, m%5
@@ -1142,4 +1264,28 @@ var RyuInvPowersOfTen = [...]extfloat128{
11421264
{Hi: 0xf712b443bbd52b7b, Lo: 0xa5e9ec7501d523e5, Exp: -1181},
11431265
{Hi: 0xc5a890362fddbc62, Lo: 0xeb2189f734aa831e, Exp: -1184},
11441266
{Hi: 0x9e20735e8cb16382, Lo: 0x55b46e5f5d5535b1, Exp: -1187},
1267+
{Hi: 0xfd00b897478238d0, Lo: 0x8920b098955522b5, Exp: -1191},
1268+
{Hi: 0xca66fa129f9b60a6, Lo: 0xd41a26e077774ef7, Exp: -1194},
1269+
{Hi: 0xa1ebfb4219491a1f, Lo: 0x1014ebe6c5f90bf9, Exp: -1197},
1270+
{Hi: 0x818995ce7aa0e1b2, Lo: 0x7343efebd1940994, Exp: -1200},
1271+
{Hi: 0xcf42894a5dce35ea, Lo: 0x52064cac828675ba, Exp: -1204},
1272+
{Hi: 0xa5ced43b7e3e9188, Lo: 0x419ea3bd35385e2e, Exp: -1207},
1273+
{Hi: 0x84a57695fe98746d, Lo: 0x014bb630f7604b58, Exp: -1210},
1274+
{Hi: 0xd43bf0effdc0ba48, Lo: 0x0212bd1b2566def3, Exp: -1214},
1275+
{Hi: 0xa9c98d8ccb009506, Lo: 0x680efdaf511f18c3, Exp: -1217},
1276+
{Hi: 0x87d4713d6f33aa6b, Lo: 0x8672648c40e5ad69, Exp: -1220},
1277+
{Hi: 0xd953e8624b85dd78, Lo: 0xd71d6dad34a2af0e, Exp: -1224},
1278+
{Hi: 0xaddcb9e83c6b1793, Lo: 0xdf4abe242a1bbf3e, Exp: -1227},
1279+
{Hi: 0x8b16fb203055ac76, Lo: 0x4c3bcb5021afcc32, Exp: -1230},
1280+
{Hi: 0xde8b2b66b3bc4723, Lo: 0xad2c788035e61383, Exp: -1234},
1281+
{Hi: 0xb208ef855c969f4f, Lo: 0xbdbd2d335e51a936, Exp: -1237},
1282+
{Hi: 0x8e6d8c6ab0787f72, Lo: 0xfe30f0f5e50e20f8, Exp: -1240},
1283+
{Hi: 0xe3e27a444d8d98b7, Lo: 0xfd1b1b2308169b26, Exp: -1244},
1284+
{Hi: 0xb64ec836a47146f9, Lo: 0x9748e2826cdee285, Exp: -1247},
1285+
{Hi: 0x91d8a02bb6c10594, Lo: 0x79071b9b8a4be86a, Exp: -1250},
1286+
{Hi: 0xe95a99df8ace6f53, Lo: 0xf4d82c2c107973dd, Exp: -1254},
1287+
{Hi: 0xbaaee17fa23ebf76, Lo: 0x5d79bcf00d2df64a, Exp: -1257},
1288+
{Hi: 0x9558b4661b6565f8, Lo: 0x4ac7ca59a424c508, Exp: -1260},
1289+
{Hi: 0xeef453d6923bd65a, Lo: 0x113faa2906a13b40, Exp: -1264},
1290+
{Hi: 0xbf29dcaba82fdeae, Lo: 0x7432ee873880fc34, Exp: -1267},
11451291
}

0 commit comments

Comments
 (0)