mirror of
https://github.com/golang/go
synced 2024-11-18 09:04:49 -07:00
math/big: remove (*Float).Scan, ScanFloat; more robust (*Float).Parse
- (*Float).Scan conflicted with fmt.Scanner.Scan; it was also only used internally. Removed it, as well as the companion ScanFloat function. - (*Float).Parse (and thus ParseFloat) can now also parse infinities. As a result, more code could be simplified. - Fixed a bug in rounding (round may implicitly be called for infinite values). Found via existing test cases, after simplifying some code. - Added more test cases. Fixes issue #10938. Change-Id: I1df97821654f034965ba8b82b272e52e6dc427f1 Reviewed-on: https://go-review.googlesource.com/10498 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
9b3d9230aa
commit
a63b1806aa
@ -381,14 +381,11 @@ func (x *Float) validate() {
|
|||||||
func (z *Float) round(sbit uint) {
|
func (z *Float) round(sbit uint) {
|
||||||
if debugFloat {
|
if debugFloat {
|
||||||
z.validate()
|
z.validate()
|
||||||
if z.form > finite {
|
|
||||||
panic(fmt.Sprintf("round called for non-finite value %s", z))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// z.form <= finite
|
|
||||||
|
|
||||||
z.acc = Exact
|
z.acc = Exact
|
||||||
if z.form == zero {
|
if z.form != finite {
|
||||||
|
// ±0 or ±Inf => nothing left to do
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// z.form == finite && len(z.mant) > 0
|
// z.form == finite && len(z.mant) > 0
|
||||||
|
@ -92,24 +92,11 @@ func TestFloatZeroValue(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeFloat(s string) *Float {
|
func makeFloat(s string) *Float {
|
||||||
var x Float
|
x, _, err := ParseFloat(s, 0, 1000, ToNearestEven)
|
||||||
|
if err != nil {
|
||||||
switch s {
|
panic(err)
|
||||||
case "0":
|
|
||||||
return &x
|
|
||||||
case "-0":
|
|
||||||
return x.Neg(&x)
|
|
||||||
case "Inf", "+Inf":
|
|
||||||
return x.SetInf(false)
|
|
||||||
case "-Inf":
|
|
||||||
return x.SetInf(true)
|
|
||||||
}
|
}
|
||||||
|
return x
|
||||||
x.SetPrec(1000)
|
|
||||||
if _, ok := x.SetString(s); !ok {
|
|
||||||
panic(fmt.Sprintf("%q is not a valid float", s))
|
|
||||||
}
|
|
||||||
return &x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFloatSetPrec(t *testing.T) {
|
func TestFloatSetPrec(t *testing.T) {
|
||||||
|
@ -14,60 +14,19 @@ import (
|
|||||||
|
|
||||||
// SetString sets z to the value of s and returns z and a boolean indicating
|
// SetString sets z to the value of s and returns z and a boolean indicating
|
||||||
// success. s must be a floating-point number of the same format as accepted
|
// success. s must be a floating-point number of the same format as accepted
|
||||||
// by Scan, with number prefixes permitted.
|
// by Parse, with base argument 0.
|
||||||
func (z *Float) SetString(s string) (*Float, bool) {
|
func (z *Float) SetString(s string) (*Float, bool) {
|
||||||
r := strings.NewReader(s)
|
if f, _, err := z.Parse(s, 0); err == nil {
|
||||||
|
return f, true
|
||||||
f, _, err := z.Scan(r, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
}
|
||||||
|
return nil, false
|
||||||
// there should be no unread characters left
|
|
||||||
if _, err = r.ReadByte(); err != io.EOF {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return f, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan scans the number corresponding to the longest possible prefix
|
// scan is like Parse but reads the longest possible prefix representing a valid
|
||||||
// of r representing a floating-point number with a mantissa in the
|
// floating point number from an io.ByteScanner rather than a string. It serves
|
||||||
// given conversion base (the exponent is always a decimal number).
|
// as the implementation of Parse. It does not recognize ±Inf and does not expect
|
||||||
// It sets z to the (possibly rounded) value of the corresponding
|
// EOF at the end.
|
||||||
// floating-point number, and returns z, the actual base b, and an
|
func (z *Float) scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
|
||||||
// error err, if any. If z's precision is 0, it is changed to 64
|
|
||||||
// before rounding takes effect. The number must be of the form:
|
|
||||||
//
|
|
||||||
// number = [ sign ] [ prefix ] mantissa [ exponent ] .
|
|
||||||
// sign = "+" | "-" .
|
|
||||||
// prefix = "0" ( "x" | "X" | "b" | "B" ) .
|
|
||||||
// mantissa = digits | digits "." [ digits ] | "." digits .
|
|
||||||
// exponent = ( "E" | "e" | "p" ) [ sign ] digits .
|
|
||||||
// digits = digit { digit } .
|
|
||||||
// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
|
|
||||||
//
|
|
||||||
// The base argument must be 0, 2, 10, or 16. Providing an invalid base
|
|
||||||
// argument will lead to a run-time panic.
|
|
||||||
//
|
|
||||||
// For base 0, the number prefix determines the actual base: A prefix of
|
|
||||||
// "0x" or "0X" selects base 16, and a "0b" or "0B" prefix selects
|
|
||||||
// base 2; otherwise, the actual base is 10 and no prefix is accepted.
|
|
||||||
// The octal prefix "0" is not supported (a leading "0" is simply
|
|
||||||
// considered a "0").
|
|
||||||
//
|
|
||||||
// A "p" exponent indicates a binary (rather then decimal) exponent;
|
|
||||||
// for instance "0x1.fffffffffffffp1023" (using base 0) represents the
|
|
||||||
// maximum float64 value. For hexadecimal mantissae, the exponent must
|
|
||||||
// be binary, if present (an "e" or "E" exponent indicator cannot be
|
|
||||||
// distinguished from a mantissa digit).
|
|
||||||
//
|
|
||||||
// The returned *Float f is nil and the value of z is valid but not
|
|
||||||
// defined if an error is reported.
|
|
||||||
//
|
|
||||||
// BUG(gri) The Float.Scan signature conflicts with Scan(s fmt.ScanState, ch rune) error.
|
|
||||||
// (https://github.com/golang/go/issues/10938)
|
|
||||||
func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
|
|
||||||
prec := z.prec
|
prec := z.prec
|
||||||
if prec == 0 {
|
if prec == 0 {
|
||||||
prec = 64
|
prec = 64
|
||||||
@ -211,14 +170,55 @@ func (z *Float) pow10(n int64) *Float {
|
|||||||
return z
|
return z
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse is like z.Scan(r, base), but instead of reading from an
|
// Parse parses s which must contain a text representation of a floating-
|
||||||
// io.ByteScanner, it parses the string s. An error is also returned
|
// point number with a mantissa in the given conversion base (the exponent
|
||||||
// if the string contains invalid or trailing bytes not belonging to
|
// is always a decimal number), or a string representing an infinite value.
|
||||||
// the number.
|
//
|
||||||
|
// It sets z to the (possibly rounded) value of the corresponding floating-
|
||||||
|
// point value, and returns z, the actual base b, and an error err, if any.
|
||||||
|
// If z's precision is 0, it is changed to 64 before rounding takes effect.
|
||||||
|
// The number must be of the form:
|
||||||
|
//
|
||||||
|
// number = [ sign ] [ prefix ] mantissa [ exponent ] | infinity .
|
||||||
|
// sign = "+" | "-" .
|
||||||
|
// prefix = "0" ( "x" | "X" | "b" | "B" ) .
|
||||||
|
// mantissa = digits | digits "." [ digits ] | "." digits .
|
||||||
|
// exponent = ( "E" | "e" | "p" ) [ sign ] digits .
|
||||||
|
// digits = digit { digit } .
|
||||||
|
// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
|
||||||
|
// infinity = [ sign ] ( "inf" | "Inf" ) .
|
||||||
|
//
|
||||||
|
// The base argument must be 0, 2, 10, or 16. Providing an invalid base
|
||||||
|
// argument will lead to a run-time panic.
|
||||||
|
//
|
||||||
|
// For base 0, the number prefix determines the actual base: A prefix of
|
||||||
|
// "0x" or "0X" selects base 16, and a "0b" or "0B" prefix selects
|
||||||
|
// base 2; otherwise, the actual base is 10 and no prefix is accepted.
|
||||||
|
// The octal prefix "0" is not supported (a leading "0" is simply
|
||||||
|
// considered a "0").
|
||||||
|
//
|
||||||
|
// A "p" exponent indicates a binary (rather then decimal) exponent;
|
||||||
|
// for instance "0x1.fffffffffffffp1023" (using base 0) represents the
|
||||||
|
// maximum float64 value. For hexadecimal mantissae, the exponent must
|
||||||
|
// be binary, if present (an "e" or "E" exponent indicator cannot be
|
||||||
|
// distinguished from a mantissa digit).
|
||||||
|
//
|
||||||
|
// The returned *Float f is nil and the value of z is valid but not
|
||||||
|
// defined if an error is reported.
|
||||||
|
//
|
||||||
func (z *Float) Parse(s string, base int) (f *Float, b int, err error) {
|
func (z *Float) Parse(s string, base int) (f *Float, b int, err error) {
|
||||||
r := strings.NewReader(s)
|
// scan doesn't handle ±Inf
|
||||||
|
if len(s) == 3 && (s == "Inf" || s == "inf") {
|
||||||
|
f = z.SetInf(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(s) == 4 && (s[0] == '+' || s[0] == '-') && (s[1:] == "Inf" || s[1:] == "inf") {
|
||||||
|
f = z.SetInf(s[0] == '-')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if f, b, err = z.Scan(r, base); err != nil {
|
r := strings.NewReader(s)
|
||||||
|
if f, b, err = z.scan(r, base); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,12 +232,6 @@ func (z *Float) Parse(s string, base int) (f *Float, b int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanFloat is like f.Scan(r, base) with f set to the given precision
|
|
||||||
// and rounding mode.
|
|
||||||
func ScanFloat(r io.ByteScanner, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) {
|
|
||||||
return new(Float).SetPrec(prec).SetMode(mode).Scan(r, base)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFloat is like f.Parse(s, base) with f set to the given precision
|
// ParseFloat is like f.Parse(s, base) with f set to the given precision
|
||||||
// and rounding mode.
|
// and rounding mode.
|
||||||
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) {
|
||||||
|
@ -11,9 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFloatSetFloat64String(t *testing.T) {
|
func TestFloatSetFloat64String(t *testing.T) {
|
||||||
|
inf := math.Inf(0)
|
||||||
|
nan := math.NaN()
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
s string
|
s string
|
||||||
x float64
|
x float64 // NaNs represent invalid inputs
|
||||||
}{
|
}{
|
||||||
// basics
|
// basics
|
||||||
{"0", 0},
|
{"0", 0},
|
||||||
@ -45,6 +48,25 @@ func TestFloatSetFloat64String(t *testing.T) {
|
|||||||
{"1.E+10", 1e10},
|
{"1.E+10", 1e10},
|
||||||
{"+1E-10", 1e-10},
|
{"+1E-10", 1e-10},
|
||||||
|
|
||||||
|
// infinities
|
||||||
|
{"Inf", inf},
|
||||||
|
{"+Inf", inf},
|
||||||
|
{"-Inf", -inf},
|
||||||
|
{"inf", inf},
|
||||||
|
{"+inf", inf},
|
||||||
|
{"-inf", -inf},
|
||||||
|
|
||||||
|
// invalid numbers
|
||||||
|
{"", nan},
|
||||||
|
{"-", nan},
|
||||||
|
{"0x", nan},
|
||||||
|
{"0e", nan},
|
||||||
|
{"1.2ef", nan},
|
||||||
|
{"2..3", nan},
|
||||||
|
{"123..", nan},
|
||||||
|
{"infinity", nan},
|
||||||
|
{"foobar", nan},
|
||||||
|
|
||||||
// misc decimal values
|
// misc decimal values
|
||||||
{"3.14159265", 3.14159265},
|
{"3.14159265", 3.14159265},
|
||||||
{"-687436.79457e-245", -687436.79457e-245},
|
{"-687436.79457e-245", -687436.79457e-245},
|
||||||
@ -96,8 +118,16 @@ func TestFloatSetFloat64String(t *testing.T) {
|
|||||||
var x Float
|
var x Float
|
||||||
x.SetPrec(53)
|
x.SetPrec(53)
|
||||||
_, ok := x.SetString(test.s)
|
_, ok := x.SetString(test.s)
|
||||||
|
if math.IsNaN(test.x) {
|
||||||
|
// test.s is invalid
|
||||||
|
if ok {
|
||||||
|
t.Errorf("%s: want parse error", test.s)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// test.s is valid
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("%s: parse error", test.s)
|
t.Errorf("%s: got parse error", test.s)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f, _ := x.Float64()
|
f, _ := x.Float64()
|
||||||
|
Loading…
Reference in New Issue
Block a user