From: Robert Griesemer Date: Tue, 26 May 2015 21:57:05 +0000 (-0700) Subject: math/big: removed TODO, cleanups X-Git-Tag: go1.5beta1~460 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0858d8847d0cdcc03ecb336c208706516bc0ab40;p=gostls13.git math/big: removed TODO, cleanups - 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 --- diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go index 5ab75e9031..dc62b450db 100644 --- a/src/math/big/floatconv.go +++ b/src/math/big/floatconv.go @@ -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) -} diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go index 6ba15754e5..db300314f1 100644 --- a/src/math/big/floatconv_test.go +++ b/src/math/big/floatconv_test.go @@ -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 { diff --git a/src/math/big/ftoa.go b/src/math/big/ftoa.go index 0a9edfd7b2..0f943e1ff2 100644 --- a/src/math/big/ftoa.go +++ b/src/math/big/ftoa.go @@ -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