]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: removed TODO, cleanups
authorRobert Griesemer <gri@golang.org>
Tue, 26 May 2015 21:57:05 +0000 (14:57 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 27 May 2015 22:09:47 +0000 (22:09 +0000)
- factor out handling of sign
- rename bstring, pstring to fmtB, fmtP consistent with fmtE, fmtF
- move all float-to-string conversion functions into ftoa.go
- no functional changes

Change-Id: I5970ecb874dc9c387630b59147d90bda16a5d8e6
Reviewed-on: https://go-review.googlesource.com/10387
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/floatconv.go
src/math/big/floatconv_test.go
src/math/big/ftoa.go

index 5ab75e90310cc665902fef634fcb5dedf4fc5960..dc62b450db031628baea1990656866dd0c5d77ee 100644 (file)
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// This file implements float-to-string conversion functions.
+// This file implements string-to-Float conversion functions.
 
 package big
 
 import (
        "fmt"
        "io"
-       "strconv"
        "strings"
 )
 
@@ -244,136 +243,3 @@ func ScanFloat(r io.ByteScanner, base int, prec uint, mode RoundingMode) (f *Flo
 func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
        return new(Float).SetPrec(prec).SetMode(mode).Parse(s, base)
 }
-
-// Format converts the floating-point number x to a string according
-// to the given format and precision prec. The format is one of:
-//
-//     'e'     -d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits
-//     'E'     -d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits
-//     'f'     -ddddd.dddd, no exponent
-//     'g'     like 'e' for large exponents, like 'f' otherwise
-//     'G'     like 'E' for large exponents, like 'f' otherwise
-//     'b'     -ddddddp±dd, binary exponent
-//     'p'     -0x.dddp±dd, binary exponent, hexadecimal mantissa
-//
-// For the binary exponent formats, the mantissa is printed in normalized form:
-//
-//     'b'     decimal integer mantissa using x.Prec() bits, or -0
-//     'p'     hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0
-//
-// The precision prec controls the number of digits (excluding the exponent)
-// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f'
-// it is the number of digits after the decimal point. For 'g' and 'G' it is
-// the total number of digits. A negative precision selects the smallest
-// number of digits necessary such that ParseFloat will return f exactly.
-// The prec value is ignored for the 'b' or 'p' format.
-//
-// BUG(gri) Float.Format does not accept negative precisions.
-// BUG(gri) The Float.Format signature conflicts with Format(f fmt.State, c rune).
-//          (https://github.com/golang/go/issues/10938)
-func (x *Float) Format(format byte, prec int) string {
-       const extra = 10 // TODO(gri) determine a good/better value here
-       return string(x.Append(make([]byte, 0, prec+extra), format, prec))
-}
-
-// Append appends the string form of the floating-point number x,
-// as generated by x.Format, to buf and returns the extended buffer.
-func (x *Float) Append(buf []byte, format byte, prec int) []byte {
-       // TODO(gri) factor out handling of sign?
-
-       // Inf
-       if x.IsInf() {
-               var ch byte = '+'
-               if x.neg {
-                       ch = '-'
-               }
-               buf = append(buf, ch)
-               return append(buf, "Inf"...)
-       }
-
-       // easy formats
-       switch format {
-       case 'b':
-               return x.bstring(buf)
-       case 'p':
-               return x.pstring(buf)
-       }
-
-       return x.bigFtoa(buf, format, prec)
-}
-
-// BUG(gri): Float.String uses x.Format('g', 10) rather than x.Format('g', -1).
-func (x *Float) String() string {
-       return x.Format('g', 10)
-}
-
-// bstring appends the string of x in the format ["-"] mantissa "p" exponent
-// with a decimal mantissa and a binary exponent, or ["-"] "0" if x is zero,
-// and returns the extended buffer.
-// The mantissa is normalized such that is uses x.Prec() bits in binary
-// representation.
-func (x *Float) bstring(buf []byte) []byte {
-       if x.neg {
-               buf = append(buf, '-')
-       }
-       if x.form == zero {
-               return append(buf, '0')
-       }
-
-       if debugFloat && x.form != finite {
-               panic("non-finite float")
-       }
-       // x != 0
-
-       // adjust mantissa to use exactly x.prec bits
-       m := x.mant
-       switch w := uint32(len(x.mant)) * _W; {
-       case w < x.prec:
-               m = nat(nil).shl(m, uint(x.prec-w))
-       case w > x.prec:
-               m = nat(nil).shr(m, uint(w-x.prec))
-       }
-
-       buf = append(buf, m.decimalString()...)
-       buf = append(buf, 'p')
-       e := int64(x.exp) - int64(x.prec)
-       if e >= 0 {
-               buf = append(buf, '+')
-       }
-       return strconv.AppendInt(buf, e, 10)
-}
-
-// pstring appends the string of x in the format ["-"] "0x." mantissa "p" exponent
-// with a hexadecimal mantissa and a binary exponent, or ["-"] "0" if x is zero,
-// ad returns the extended buffer.
-// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
-func (x *Float) pstring(buf []byte) []byte {
-       if x.neg {
-               buf = append(buf, '-')
-       }
-       if x.form == zero {
-               return append(buf, '0')
-       }
-
-       if debugFloat && x.form != finite {
-               panic("non-finite float")
-       }
-       // x != 0
-
-       // remove trailing 0 words early
-       // (no need to convert to hex 0's and trim later)
-       m := x.mant
-       i := 0
-       for i < len(m) && m[i] == 0 {
-               i++
-       }
-       m = m[i:]
-
-       buf = append(buf, "0x."...)
-       buf = append(buf, strings.TrimRight(x.mant.hexString(), "0")...)
-       buf = append(buf, 'p')
-       if x.exp >= 0 {
-               buf = append(buf, '+')
-       }
-       return strconv.AppendInt(buf, int64(x.exp), 10)
-}
index 6ba15754e5df9934a0242728f56c517fe8481a34..db300314f1d9f831c28a46da8d2f156497bb6a48 100644 (file)
@@ -366,6 +366,7 @@ func TestFloatFormat(t *testing.T) {
 
                // unsupported format
                {"3.14", 64, 'x', 0, "%x"},
+               {"-3.14", 64, 'x', 0, "%x"},
        } {
                f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven)
                if err != nil {
index 0a9edfd7b22330586dc29290f9609ca1a040c36d..0f943e1ff27ea1f67fd5a8bb8db259f8abd6e737 100644 (file)
@@ -2,34 +2,89 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// This file implements the 'e', 'f', 'g' floating-point formats.
-// It is closely following the corresponding implementation in
-// strconv/ftoa.go, but modified and simplified for big.Float.
-
-// Algorithm:
-//   1) convert Float to multiprecision decimal
-//   2) round to desired precision
-//   3) read digits out and format
+// This file implements Float-to-string conversion functions.
+// It is closely following the corresponding implementation
+// in strconv/ftoa.go, but modified and simplified for Float.
 
 package big
 
-import "strconv"
+import (
+       "strconv"
+       "strings"
+)
 
-// TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner.
+// Format converts the floating-point number x to a string according
+// to the given format and precision prec. The format is one of:
+//
+//     'e'     -d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits
+//     'E'     -d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits
+//     'f'     -ddddd.dddd, no exponent
+//     'g'     like 'e' for large exponents, like 'f' otherwise
+//     'G'     like 'E' for large exponents, like 'f' otherwise
+//     'b'     -ddddddp±dd, binary exponent
+//     'p'     -0x.dddp±dd, binary exponent, hexadecimal mantissa
+//
+// For the binary exponent formats, the mantissa is printed in normalized form:
+//
+//     'b'     decimal integer mantissa using x.Prec() bits, or -0
+//     'p'     hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0
+//
+// The precision prec controls the number of digits (excluding the exponent)
+// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f'
+// it is the number of digits after the decimal point. For 'g' and 'G' it is
+// the total number of digits. A negative precision selects the smallest
+// number of digits necessary such that ParseFloat will return f exactly.
+// The prec value is ignored for the 'b' or 'p' format.
+//
+// BUG(gri) Float.Format does not accept negative precisions.
+// BUG(gri) The Float.Format signature conflicts with Format(f fmt.State, c rune).
+//          (https://github.com/golang/go/issues/10938)
+func (x *Float) Format(format byte, prec int) string {
+       const extra = 10 // TODO(gri) determine a good/better value here
+       return string(x.Append(make([]byte, 0, prec+extra), format, prec))
+}
 
-// bigFtoa formats a float for the %e, %E, %f, %g, and %G formats.
-func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
-       if debugFloat && f.IsInf() {
-               panic("non-finite float")
+// String formats x like x.Format('g', 10).
+func (x *Float) String() string {
+       return x.Format('g', 10)
+}
+
+// Append appends to buf the string form of the floating-point number x,
+// as generated by x.Format, and returns the extended buffer.
+func (x *Float) Append(buf []byte, fmt byte, prec int) []byte {
+       // sign
+       if x.neg {
+               buf = append(buf, '-')
+       }
+
+       // Inf
+       if x.IsInf() {
+               if !x.neg {
+                       buf = append(buf, '+')
+               }
+               return append(buf, "Inf"...)
+       }
+
+       // pick off easy formats
+       switch fmt {
+       case 'b':
+               return x.fmtB(buf)
+       case 'p':
+               return x.fmtP(buf)
        }
 
+       // Algorithm:
+       //   1) convert Float to multiprecision decimal
+       //   2) round to desired precision
+       //   3) read digits out and format
+
        // 1) convert Float to multiprecision decimal
        var mant nat
-       if f.form == finite {
-               mant = f.mant
+       if x.form == finite {
+               mant = x.mant
        }
        var d decimal
-       d.init(mant, int(f.exp)-f.mant.bitLen())
+       d.init(mant, int(x.exp)-x.mant.bitLen())
 
        // 2) round to desired precision
        shortest := false
@@ -67,9 +122,9 @@ func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
        // 3) read digits out and format
        switch fmt {
        case 'e', 'E':
-               return fmtE(buf, fmt, prec, f.neg, d)
+               return fmtE(buf, fmt, prec, d)
        case 'f':
-               return fmtF(buf, prec, f.neg, d)
+               return fmtF(buf, prec, d)
        case 'g', 'G':
                // trim trailing fractional zeros in %e format
                eprec := prec
@@ -88,25 +143,23 @@ func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
                        if prec > len(d.mant) {
                                prec = len(d.mant)
                        }
-                       return fmtE(buf, fmt+'e'-'g', prec-1, f.neg, d)
+                       return fmtE(buf, fmt+'e'-'g', prec-1, d)
                }
                if prec > d.exp {
                        prec = len(d.mant)
                }
-               return fmtF(buf, max(prec-d.exp, 0), f.neg, d)
+               return fmtF(buf, max(prec-d.exp, 0), d)
        }
 
        // unknown format
+       if x.neg {
+               buf = buf[:len(buf)-1] // sign was added prematurely - remove it again
+       }
        return append(buf, '%', fmt)
 }
 
-// %e: -d.ddddde±dd
-func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte {
-       // sign
-       if neg {
-               buf = append(buf, '-')
-       }
-
+// %e: d.ddddde±dd
+func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte {
        // first digit
        ch := byte('0')
        if len(d.mant) > 0 {
@@ -149,13 +202,8 @@ func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte {
        return strconv.AppendInt(buf, exp, 10)
 }
 
-// %f: -ddddddd.ddddd
-func fmtF(buf []byte, prec int, neg bool, d decimal) []byte {
-       // sign
-       if neg {
-               buf = append(buf, '-')
-       }
-
+// %f: ddddddd.ddddd
+func fmtF(buf []byte, prec int, d decimal) []byte {
        // integer, padded with zeros as needed
        if d.exp > 0 {
                m := min(len(d.mant), d.exp)
@@ -182,6 +230,73 @@ func fmtF(buf []byte, prec int, neg bool, d decimal) []byte {
        return buf
 }
 
+// fmtB appends the string of x in the format mantissa "p" exponent
+// with a decimal mantissa and a binary exponent, or 0" if x is zero,
+// and returns the extended buffer.
+// The mantissa is normalized such that is uses x.Prec() bits in binary
+// representation.
+// The sign of x is ignored, and x must not be an Inf.
+func (x *Float) fmtB(buf []byte) []byte {
+       if x.form == zero {
+               return append(buf, '0')
+       }
+
+       if debugFloat && x.form != finite {
+               panic("non-finite float")
+       }
+       // x != 0
+
+       // adjust mantissa to use exactly x.prec bits
+       m := x.mant
+       switch w := uint32(len(x.mant)) * _W; {
+       case w < x.prec:
+               m = nat(nil).shl(m, uint(x.prec-w))
+       case w > x.prec:
+               m = nat(nil).shr(m, uint(w-x.prec))
+       }
+
+       buf = append(buf, m.decimalString()...)
+       buf = append(buf, 'p')
+       e := int64(x.exp) - int64(x.prec)
+       if e >= 0 {
+               buf = append(buf, '+')
+       }
+       return strconv.AppendInt(buf, e, 10)
+}
+
+// fmtP appends the string of x in the format 0x." mantissa "p" exponent
+// with a hexadecimal mantissa and a binary exponent, or 0" if x is zero,
+// ad returns the extended buffer.
+// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0.
+// The sign of x is ignored, and x must not be an Inf.
+func (x *Float) fmtP(buf []byte) []byte {
+       if x.form == zero {
+               return append(buf, '0')
+       }
+
+       if debugFloat && x.form != finite {
+               panic("non-finite float")
+       }
+       // x != 0
+
+       // remove trailing 0 words early
+       // (no need to convert to hex 0's and trim later)
+       m := x.mant
+       i := 0
+       for i < len(m) && m[i] == 0 {
+               i++
+       }
+       m = m[i:]
+
+       buf = append(buf, "0x."...)
+       buf = append(buf, strings.TrimRight(x.mant.hexString(), "0")...)
+       buf = append(buf, 'p')
+       if x.exp >= 0 {
+               buf = append(buf, '+')
+       }
+       return strconv.AppendInt(buf, int64(x.exp), 10)
+}
+
 func min(x, y int) int {
        if x < y {
                return x