From: Russ Cox Date: Tue, 29 Jan 2019 21:25:54 +0000 (-0500) Subject: strconv: parse hex floats X-Git-Tag: go1.13beta1~1482 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=07717247d8decf5a5793f04c368eab3f43fad44f;p=gostls13.git 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 #29008. Change-Id: I45f3b0c36b5d92c0e8a4b35c05443a83d7a6d4b3 Reviewed-on: https://go-review.googlesource.com/c/160241 Run-TryBot: Russ Cox TryBot-Result: Gobot Gobot Reviewed-by: Robert Griesemer --- diff --git a/src/strconv/atof.go b/src/strconv/atof.go index ada85e9fed..3ced3c7167 100644 --- a/src/strconv/atof.go +++ b/src/strconv/atof.go @@ -12,7 +12,7 @@ package strconv import "math" -var optimize = true // can change for testing +var optimize = true // set to false to force slow-path conversions for testing func equalIgnoreCase(s1, s2 string) bool { if len(s1) != len(s2) { @@ -119,7 +119,7 @@ func (b *decimal) set(s string) (ok bool) { // just be sure to move the decimal point by // a lot (say, 100000). it doesn't matter if it's // not the exact number. - if i < len(s) && (s[i] == 'e' || s[i] == 'E') { + if i < len(s) && lower(s[i]) == 'e' { i++ if i >= len(s) { return @@ -152,10 +152,9 @@ func (b *decimal) set(s string) (ok bool) { } // readFloat reads a decimal mantissa and exponent from a float -// string representation. It sets ok to false if the number could +// string representation. It returns ok==false if the number could // not fit return types or is invalid. -func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { - const uint64digits = 19 +func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) { i := 0 // optional sign @@ -171,6 +170,16 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { } // digits + base := uint64(10) + maxMantDigits := 19 // 10^19 fits in uint64 + expChar := byte('e') + if i+2 < len(s) && s[i] == '0' && lower(s[i+1]) == 'x' { + base = 16 + maxMantDigits = 16 // 16^16 fits in uint64 + i += 2 + expChar = 'p' + hex = true + } sawdot := false sawdigits := false nd := 0 @@ -193,11 +202,23 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { continue } nd++ - if ndMant < uint64digits { - mantissa *= 10 + if ndMant < maxMantDigits { + mantissa *= base mantissa += uint64(c - '0') ndMant++ - } else if s[i] != '0' { + } else if c != '0' { + trunc = true + } + continue + + case base == 16 && 'a' <= lower(c) && lower(c) <= 'f': + sawdigits = true + nd++ + if ndMant < maxMantDigits { + mantissa *= 16 + mantissa += uint64(lower(c) - 'a' + 10) + ndMant++ + } else { trunc = true } continue @@ -211,12 +232,17 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { dp = nd } + if base == 16 { + dp *= 4 + ndMant *= 4 + } + // optional exponent moves decimal point. // if we read a very large, very long number, // just be sure to move the decimal point by // a lot (say, 100000). it doesn't matter if it's // not the exact number. - if i < len(s) && (s[i] == 'e' || s[i] == 'E') { + if i < len(s) && lower(s[i]) == expChar { i++ if i >= len(s) { return @@ -238,6 +264,9 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { } } dp += e * esign + } else if base == 16 { + // Must have exponent. + return } if i != len(s) { @@ -249,7 +278,6 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { } ok = true return - } // decimal power of ten to binary power of two. @@ -433,6 +461,76 @@ func atof32exact(mantissa uint64, exp int, neg bool) (f float32, ok bool) { return } +// atofHex converts the hex floating-point string s +// to a rounded float32 or float64 value (depending on flt==&float32info or flt==&float64info) +// and returns it as a float64. +// The string s has already been parsed into a mantissa, exponent, and sign (neg==true for negative). +// If trunc is true, trailing non-zero bits have been omitted from the mantissa. +func atofHex(s string, flt *floatInfo, mantissa uint64, exp int, neg, trunc bool) (float64, error) { + maxExp := 1<>(flt.mantbits+2) == 0 { + mantissa <<= 1 + exp-- + } + if trunc { + mantissa |= 1 + } + for mantissa>>(1+flt.mantbits+2) != 0 { + mantissa = mantissa>>1 | mantissa&1 + exp++ + } + + // If exponent is too negative, + // denormalize in hopes of making it representable. + // (The -2 is for the rounding bits.) + for mantissa > 1 && exp < minExp-2 { + mantissa = mantissa>>1 | mantissa&1 + exp++ + } + + // Round using two bottom bits. + round := mantissa & 3 + mantissa >>= 2 + round |= mantissa & 1 // round to even (round up if mantissa is odd) + exp += 2 + if round == 3 { + mantissa++ + if mantissa == 1<<(1+flt.mantbits) { + mantissa >>= 1 + exp++ + } + } + + if mantissa>>flt.mantbits == 0 { // Denormal or zero. + exp = flt.bias + } + var err error + if exp > maxExp { // infinity and range error + mantissa = 1 << flt.mantbits + exp = maxExp + 1 + err = rangeError(fnParseFloat, s) + } + + bits := mantissa & (1<