Skip to content

Commit b57ee70

Browse files
rscnebulabox
authored andcommitted
strconv: parse hex floats
This CL updates ParseFloat to recognize standard hexadecimal floating-point constants. See golang.org/design/19308-number-literals for background. For golang#29008. Change-Id: I45f3b0c36b5d92c0e8a4b35c05443a83d7a6d4b3 Reviewed-on: https://go-review.googlesource.com/c/160241 Run-TryBot: Russ Cox <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 63f0100 commit b57ee70

File tree

3 files changed

+341
-48
lines changed

3 files changed

+341
-48
lines changed

src/strconv/atof.go

Lines changed: 157 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ package strconv
1212

1313
import "math"
1414

15-
var optimize = true // can change for testing
15+
var optimize = true // set to false to force slow-path conversions for testing
1616

1717
func equalIgnoreCase(s1, s2 string) bool {
1818
if len(s1) != len(s2) {
@@ -119,7 +119,7 @@ func (b *decimal) set(s string) (ok bool) {
119119
// just be sure to move the decimal point by
120120
// a lot (say, 100000). it doesn't matter if it's
121121
// not the exact number.
122-
if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
122+
if i < len(s) && lower(s[i]) == 'e' {
123123
i++
124124
if i >= len(s) {
125125
return
@@ -152,10 +152,9 @@ func (b *decimal) set(s string) (ok bool) {
152152
}
153153

154154
// readFloat reads a decimal mantissa and exponent from a float
155-
// string representation. It sets ok to false if the number could
155+
// string representation. It returns ok==false if the number could
156156
// not fit return types or is invalid.
157-
func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
158-
const uint64digits = 19
157+
func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
159158
i := 0
160159

161160
// optional sign
@@ -171,6 +170,16 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
171170
}
172171

173172
// digits
173+
base := uint64(10)
174+
maxMantDigits := 19 // 10^19 fits in uint64
175+
expChar := byte('e')
176+
if i+2 < len(s) && s[i] == '0' && lower(s[i+1]) == 'x' {
177+
base = 16
178+
maxMantDigits = 16 // 16^16 fits in uint64
179+
i += 2
180+
expChar = 'p'
181+
hex = true
182+
}
174183
sawdot := false
175184
sawdigits := false
176185
nd := 0
@@ -193,11 +202,23 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
193202
continue
194203
}
195204
nd++
196-
if ndMant < uint64digits {
197-
mantissa *= 10
205+
if ndMant < maxMantDigits {
206+
mantissa *= base
198207
mantissa += uint64(c - '0')
199208
ndMant++
200-
} else if s[i] != '0' {
209+
} else if c != '0' {
210+
trunc = true
211+
}
212+
continue
213+
214+
case base == 16 && 'a' <= lower(c) && lower(c) <= 'f':
215+
sawdigits = true
216+
nd++
217+
if ndMant < maxMantDigits {
218+
mantissa *= 16
219+
mantissa += uint64(lower(c) - 'a' + 10)
220+
ndMant++
221+
} else {
201222
trunc = true
202223
}
203224
continue
@@ -211,12 +232,17 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
211232
dp = nd
212233
}
213234

235+
if base == 16 {
236+
dp *= 4
237+
ndMant *= 4
238+
}
239+
214240
// optional exponent moves decimal point.
215241
// if we read a very large, very long number,
216242
// just be sure to move the decimal point by
217243
// a lot (say, 100000). it doesn't matter if it's
218244
// not the exact number.
219-
if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
245+
if i < len(s) && lower(s[i]) == expChar {
220246
i++
221247
if i >= len(s) {
222248
return
@@ -238,6 +264,9 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
238264
}
239265
}
240266
dp += e * esign
267+
} else if base == 16 {
268+
// Must have exponent.
269+
return
241270
}
242271

243272
if i != len(s) {
@@ -249,7 +278,6 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) {
249278
}
250279
ok = true
251280
return
252-
253281
}
254282

255283
// decimal power of ten to binary power of two.
@@ -433,35 +461,109 @@ func atof32exact(mantissa uint64, exp int, neg bool) (f float32, ok bool) {
433461
return
434462
}
435463

464+
// atofHex converts the hex floating-point string s
465+
// to a rounded float32 or float64 value (depending on flt==&float32info or flt==&float64info)
466+
// and returns it as a float64.
467+
// The string s has already been parsed into a mantissa, exponent, and sign (neg==true for negative).
468+
// If trunc is true, trailing non-zero bits have been omitted from the mantissa.
469+
func atofHex(s string, flt *floatInfo, mantissa uint64, exp int, neg, trunc bool) (float64, error) {
470+
maxExp := 1<<flt.expbits + flt.bias - 2
471+
minExp := flt.bias + 1
472+
exp += int(flt.mantbits) // mantissa now implicitly divided by 2^mantbits.
473+
474+
// Shift mantissa and exponent to bring representation into float range.
475+
// Eventually we want a mantissa with a leading 1-bit followed by mantbits other bits.
476+
// For rounding, we need two more, where the bottom bit represents
477+
// whether that bit or any later bit was non-zero.
478+
// (If the mantissa has already lost non-zero bits, trunc is true,
479+
// and we OR in a 1 below after shifting left appropriately.)
480+
for mantissa != 0 && mantissa>>(flt.mantbits+2) == 0 {
481+
mantissa <<= 1
482+
exp--
483+
}
484+
if trunc {
485+
mantissa |= 1
486+
}
487+
for mantissa>>(1+flt.mantbits+2) != 0 {
488+
mantissa = mantissa>>1 | mantissa&1
489+
exp++
490+
}
491+
492+
// If exponent is too negative,
493+
// denormalize in hopes of making it representable.
494+
// (The -2 is for the rounding bits.)
495+
for mantissa > 1 && exp < minExp-2 {
496+
mantissa = mantissa>>1 | mantissa&1
497+
exp++
498+
}
499+
500+
// Round using two bottom bits.
501+
round := mantissa & 3
502+
mantissa >>= 2
503+
round |= mantissa & 1 // round to even (round up if mantissa is odd)
504+
exp += 2
505+
if round == 3 {
506+
mantissa++
507+
if mantissa == 1<<(1+flt.mantbits) {
508+
mantissa >>= 1
509+
exp++
510+
}
511+
}
512+
513+
if mantissa>>flt.mantbits == 0 { // Denormal or zero.
514+
exp = flt.bias
515+
}
516+
var err error
517+
if exp > maxExp { // infinity and range error
518+
mantissa = 1 << flt.mantbits
519+
exp = maxExp + 1
520+
err = rangeError(fnParseFloat, s)
521+
}
522+
523+
bits := mantissa & (1<<flt.mantbits - 1)
524+
bits |= uint64((exp-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits
525+
if neg {
526+
bits |= 1 << flt.mantbits << flt.expbits
527+
}
528+
if flt == &float32info {
529+
return float64(math.Float32frombits(uint32(bits))), err
530+
}
531+
return math.Float64frombits(bits), err
532+
}
533+
436534
const fnParseFloat = "ParseFloat"
437535

438536
func atof32(s string) (f float32, err error) {
439537
if val, ok := special(s); ok {
440538
return float32(val), nil
441539
}
442540

443-
if optimize {
444-
// Parse mantissa and exponent.
445-
mantissa, exp, neg, trunc, ok := readFloat(s)
446-
if ok {
447-
// Try pure floating-point arithmetic conversion.
448-
if !trunc {
449-
if f, ok := atof32exact(mantissa, exp, neg); ok {
450-
return f, nil
451-
}
541+
mantissa, exp, neg, trunc, hex, ok := readFloat(s)
542+
if hex && ok {
543+
f, err := atofHex(s, &float32info, mantissa, exp, neg, trunc)
544+
return float32(f), err
545+
}
546+
547+
if optimize && ok {
548+
// Try pure floating-point arithmetic conversion.
549+
if !trunc {
550+
if f, ok := atof32exact(mantissa, exp, neg); ok {
551+
return f, nil
452552
}
453-
// Try another fast path.
454-
ext := new(extFloat)
455-
if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float32info); ok {
456-
b, ovf := ext.floatBits(&float32info)
457-
f = math.Float32frombits(uint32(b))
458-
if ovf {
459-
err = rangeError(fnParseFloat, s)
460-
}
461-
return f, err
553+
}
554+
// Try another fast path.
555+
ext := new(extFloat)
556+
if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float32info); ok {
557+
b, ovf := ext.floatBits(&float32info)
558+
f = math.Float32frombits(uint32(b))
559+
if ovf {
560+
err = rangeError(fnParseFloat, s)
462561
}
562+
return f, err
463563
}
464564
}
565+
566+
// Slow fallback.
465567
var d decimal
466568
if !d.set(s) {
467569
return 0, syntaxError(fnParseFloat, s)
@@ -479,28 +581,31 @@ func atof64(s string) (f float64, err error) {
479581
return val, nil
480582
}
481583

482-
if optimize {
483-
// Parse mantissa and exponent.
484-
mantissa, exp, neg, trunc, ok := readFloat(s)
485-
if ok {
486-
// Try pure floating-point arithmetic conversion.
487-
if !trunc {
488-
if f, ok := atof64exact(mantissa, exp, neg); ok {
489-
return f, nil
490-
}
584+
mantissa, exp, neg, trunc, hex, ok := readFloat(s)
585+
if hex && ok {
586+
return atofHex(s, &float64info, mantissa, exp, neg, trunc)
587+
}
588+
589+
if optimize && ok {
590+
// Try pure floating-point arithmetic conversion.
591+
if !trunc {
592+
if f, ok := atof64exact(mantissa, exp, neg); ok {
593+
return f, nil
491594
}
492-
// 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)
499-
}
500-
return f, err
595+
}
596+
// Try another fast path.
597+
ext := new(extFloat)
598+
if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok {
599+
b, ovf := ext.floatBits(&float64info)
600+
f = math.Float64frombits(b)
601+
if ovf {
602+
err = rangeError(fnParseFloat, s)
501603
}
604+
return f, err
502605
}
503606
}
607+
608+
// Slow fallback.
504609
var d decimal
505610
if !d.set(s) {
506611
return 0, syntaxError(fnParseFloat, s)
@@ -518,9 +623,13 @@ func atof64(s string) (f float64, err error) {
518623
// When bitSize=32, the result still has type float64, but it will be
519624
// convertible to float32 without changing its value.
520625
//
521-
// If s is well-formed and near a valid floating point number,
522-
// ParseFloat returns the nearest floating point number rounded
626+
// ParseFloat accepts decimal and hexadecimal floating-point number syntax.
627+
// If s is well-formed and near a valid floating-point number,
628+
// ParseFloat returns the nearest floating-point number rounded
523629
// using IEEE754 unbiased rounding.
630+
// (Parsing a hexadecimal floating-point value only rounds when
631+
// there are more bits in the hexadecimal representatiton than
632+
// will fit in the mantissa.)
524633
//
525634
// The errors that ParseFloat returns have concrete type *NumError
526635
// and include err.Num = s.

0 commit comments

Comments
 (0)