mirror of
https://github.com/golang/go
synced 2024-11-24 01:10:12 -07:00
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 <adonovan@google.com>
This commit is contained in:
parent
acd82d5017
commit
0858d8847d
@ -2,14 +2,13 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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
|
package big
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"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) {
|
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)
|
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)
|
|
||||||
}
|
|
||||||
|
@ -366,6 +366,7 @@ func TestFloatFormat(t *testing.T) {
|
|||||||
|
|
||||||
// unsupported format
|
// unsupported format
|
||||||
{"3.14", 64, 'x', 0, "%x"},
|
{"3.14", 64, 'x', 0, "%x"},
|
||||||
|
{"-3.14", 64, 'x', 0, "%x"},
|
||||||
} {
|
} {
|
||||||
f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven)
|
f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,34 +2,89 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// This file implements the 'e', 'f', 'g' floating-point formats.
|
// This file implements Float-to-string conversion functions.
|
||||||
// It is closely following the corresponding implementation in
|
// It is closely following the corresponding implementation
|
||||||
// strconv/ftoa.go, but modified and simplified for big.Float.
|
// in strconv/ftoa.go, but modified and simplified for Float.
|
||||||
|
|
||||||
// Algorithm:
|
|
||||||
// 1) convert Float to multiprecision decimal
|
|
||||||
// 2) round to desired precision
|
|
||||||
// 3) read digits out and format
|
|
||||||
|
|
||||||
package big
|
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.
|
// String formats x like x.Format('g', 10).
|
||||||
func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
|
func (x *Float) String() string {
|
||||||
if debugFloat && f.IsInf() {
|
return x.Format('g', 10)
|
||||||
panic("non-finite float")
|
}
|
||||||
|
|
||||||
|
// 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
|
// 1) convert Float to multiprecision decimal
|
||||||
var mant nat
|
var mant nat
|
||||||
if f.form == finite {
|
if x.form == finite {
|
||||||
mant = f.mant
|
mant = x.mant
|
||||||
}
|
}
|
||||||
var d decimal
|
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
|
// 2) round to desired precision
|
||||||
shortest := false
|
shortest := false
|
||||||
@ -67,9 +122,9 @@ func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
|
|||||||
// 3) read digits out and format
|
// 3) read digits out and format
|
||||||
switch fmt {
|
switch fmt {
|
||||||
case 'e', 'E':
|
case 'e', 'E':
|
||||||
return fmtE(buf, fmt, prec, f.neg, d)
|
return fmtE(buf, fmt, prec, d)
|
||||||
case 'f':
|
case 'f':
|
||||||
return fmtF(buf, prec, f.neg, d)
|
return fmtF(buf, prec, d)
|
||||||
case 'g', 'G':
|
case 'g', 'G':
|
||||||
// trim trailing fractional zeros in %e format
|
// trim trailing fractional zeros in %e format
|
||||||
eprec := prec
|
eprec := prec
|
||||||
@ -88,25 +143,23 @@ func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
|
|||||||
if prec > len(d.mant) {
|
if prec > len(d.mant) {
|
||||||
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 {
|
if prec > d.exp {
|
||||||
prec = len(d.mant)
|
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
|
// unknown format
|
||||||
|
if x.neg {
|
||||||
|
buf = buf[:len(buf)-1] // sign was added prematurely - remove it again
|
||||||
|
}
|
||||||
return append(buf, '%', fmt)
|
return append(buf, '%', fmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// %e: -d.ddddde±dd
|
// %e: d.ddddde±dd
|
||||||
func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte {
|
func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte {
|
||||||
// sign
|
|
||||||
if neg {
|
|
||||||
buf = append(buf, '-')
|
|
||||||
}
|
|
||||||
|
|
||||||
// first digit
|
// first digit
|
||||||
ch := byte('0')
|
ch := byte('0')
|
||||||
if len(d.mant) > 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)
|
return strconv.AppendInt(buf, exp, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
// %f: -ddddddd.ddddd
|
// %f: ddddddd.ddddd
|
||||||
func fmtF(buf []byte, prec int, neg bool, d decimal) []byte {
|
func fmtF(buf []byte, prec int, d decimal) []byte {
|
||||||
// sign
|
|
||||||
if neg {
|
|
||||||
buf = append(buf, '-')
|
|
||||||
}
|
|
||||||
|
|
||||||
// integer, padded with zeros as needed
|
// integer, padded with zeros as needed
|
||||||
if d.exp > 0 {
|
if d.exp > 0 {
|
||||||
m := min(len(d.mant), d.exp)
|
m := min(len(d.mant), d.exp)
|
||||||
@ -182,6 +230,73 @@ func fmtF(buf []byte, prec int, neg bool, d decimal) []byte {
|
|||||||
return buf
|
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 {
|
func min(x, y int) int {
|
||||||
if x < y {
|
if x < y {
|
||||||
return x
|
return x
|
||||||
|
Loading…
Reference in New Issue
Block a user