diff options
Diffstat (limited to 'libgo/go/strconv/atof.go')
-rw-r--r-- | libgo/go/strconv/atof.go | 230 |
1 files changed, 180 insertions, 50 deletions
diff --git a/libgo/go/strconv/atof.go b/libgo/go/strconv/atof.go index fdcb8b3..eff1379 100644 --- a/libgo/go/strconv/atof.go +++ b/libgo/go/strconv/atof.go @@ -13,7 +13,7 @@ package strconv import "math" import "runtime" -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) { @@ -84,6 +84,9 @@ func (b *decimal) set(s string) (ok bool) { sawdigits := false for ; i < len(s); i++ { switch { + case s[i] == '_': + // underscoreOK already called + continue case s[i] == '.': if sawdot { return @@ -120,7 +123,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 @@ -136,7 +139,11 @@ func (b *decimal) set(s string) (ok bool) { return } e := 0 - for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i++ { + if s[i] == '_' { + // underscoreOK already called + continue + } if e < 10000 { e = e*10 + int(s[i]) - '0' } @@ -153,10 +160,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 @@ -172,6 +178,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 @@ -179,6 +195,10 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { dp := 0 for ; i < len(s); i++ { switch c := s[i]; true { + case c == '_': + // underscoreOK already called + continue + case c == '.': if sawdot { return @@ -194,11 +214,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 @@ -212,12 +244,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 @@ -233,12 +270,19 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, ok bool) { return } e := 0 - for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { + for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i++ { + if s[i] == '_' { + // underscoreOK already called + continue + } if e < 10000 { e = e*10 + int(s[i]) - '0' } } dp += e * esign + } else if base == 16 { + // Must have exponent. + return } if i != len(s) { @@ -250,7 +294,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. @@ -439,6 +482,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.expbits + flt.bias - 2 + minExp := flt.bias + 1 + exp += int(flt.mantbits) // mantissa now implicitly divided by 2^mantbits. + + // Shift mantissa and exponent to bring representation into float range. + // Eventually we want a mantissa with a leading 1-bit followed by mantbits other bits. + // For rounding, we need two more, where the bottom bit represents + // whether that bit or any later bit was non-zero. + // (If the mantissa has already lost non-zero bits, trunc is true, + // and we OR in a 1 below after shifting left appropriately.) + for mantissa != 0 && mantissa>>(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<<flt.mantbits - 1) + bits |= uint64((exp-flt.bias)&(1<<flt.expbits-1)) << flt.mantbits + if neg { + bits |= 1 << flt.mantbits << flt.expbits + } + if flt == &float32info { + return float64(math.Float32frombits(uint32(bits))), err + } + return math.Float64frombits(bits), err +} + const fnParseFloat = "ParseFloat" func atof32(s string) (f float32, err error) { @@ -446,28 +559,32 @@ func atof32(s string) (f float32, err error) { return float32(val), nil } - if optimize { - // Parse mantissa and exponent. - mantissa, exp, neg, trunc, ok := readFloat(s) - if ok { - // Try pure floating-point arithmetic conversion. - if !trunc { - if f, ok := atof32exact(mantissa, exp, neg); ok { - return f, nil - } + mantissa, exp, neg, trunc, hex, ok := readFloat(s) + if hex && ok { + f, err := atofHex(s, &float32info, mantissa, exp, neg, trunc) + return float32(f), err + } + + if optimize && ok { + // Try pure floating-point arithmetic conversion. + if !trunc { + if f, ok := atof32exact(mantissa, exp, neg); ok { + return f, nil } - // Try another fast path. - ext := new(extFloat) - if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float32info); ok { - b, ovf := ext.floatBits(&float32info) - f = math.Float32frombits(uint32(b)) - if ovf { - err = rangeError(fnParseFloat, s) - } - return f, err + } + // Try another fast path. + ext := new(extFloat) + if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float32info); ok { + b, ovf := ext.floatBits(&float32info) + f = math.Float32frombits(uint32(b)) + if ovf { + err = rangeError(fnParseFloat, s) } + return f, err } } + + // Slow fallback. var d decimal if !d.set(s) { return 0, syntaxError(fnParseFloat, s) @@ -485,28 +602,31 @@ func atof64(s string) (f float64, err error) { return val, nil } - if optimize { - // Parse mantissa and exponent. - mantissa, exp, neg, trunc, ok := readFloat(s) - if ok { - // Try pure floating-point arithmetic conversion. - if !trunc { - if f, ok := atof64exact(mantissa, exp, neg); ok { - return f, nil - } + mantissa, exp, neg, trunc, hex, ok := readFloat(s) + if hex && ok { + return atofHex(s, &float64info, mantissa, exp, neg, trunc) + } + + if optimize && ok { + // Try pure floating-point arithmetic conversion. + if !trunc { + if f, ok := atof64exact(mantissa, exp, neg); ok { + return f, nil } - // Try another fast path. - ext := new(extFloat) - if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok { - b, ovf := ext.floatBits(&float64info) - f = math.Float64frombits(b) - if ovf { - err = rangeError(fnParseFloat, s) - } - return f, err + } + // Try another fast path. + ext := new(extFloat) + if ok := ext.AssignDecimal(mantissa, exp, neg, trunc, &float64info); ok { + b, ovf := ext.floatBits(&float64info) + f = math.Float64frombits(b) + if ovf { + err = rangeError(fnParseFloat, s) } + return f, err } } + + // Slow fallback. var d decimal if !d.set(s) { return 0, syntaxError(fnParseFloat, s) @@ -524,9 +644,13 @@ func atof64(s string) (f float64, err error) { // When bitSize=32, the result still has type float64, but it will be // convertible to float32 without changing its value. // -// If s is well-formed and near a valid floating point number, -// ParseFloat returns the nearest floating point number rounded +// ParseFloat accepts decimal and hexadecimal floating-point number syntax. +// If s is well-formed and near a valid floating-point number, +// ParseFloat returns the nearest floating-point number rounded // using IEEE754 unbiased rounding. +// (Parsing a hexadecimal floating-point value only rounds when +// there are more bits in the hexadecimal representatiton than +// will fit in the mantissa.) // // The errors that ParseFloat returns have concrete type *NumError // and include err.Num = s. @@ -536,7 +660,13 @@ func atof64(s string) (f float64, err error) { // If s is syntactically well-formed but is more than 1/2 ULP // away from the largest floating point number of the given size, // ParseFloat returns f = ±Inf, err.Err = ErrRange. +// +// ParseFloat recognizes the strings "NaN", "+Inf", and "-Inf" as their +// respective special floating point values. It ignores case when matching. func ParseFloat(s string, bitSize int) (float64, error) { + if !underscoreOK(s) { + return 0, syntaxError(fnParseFloat, s) + } if bitSize == 32 { f, err := atof32(s) return float64(f), err |