]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: use stringer for enum String() methods
authorRobert Griesemer <gri@golang.org>
Tue, 3 Mar 2015 22:17:39 +0000 (14:17 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 4 Mar 2015 18:24:34 +0000 (18:24 +0000)
Change-Id: Ide0615542d67b7d81bf6c56aab550e142a8789f7
Reviewed-on: https://go-review.googlesource.com/6682
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/accuracy_string.go [new file with mode: 0644]
src/math/big/float.go
src/math/big/float_test.go
src/math/big/floatconv.go
src/math/big/floatexample_test.go
src/math/big/roundingmode_string.go [new file with mode: 0644]

diff --git a/src/math/big/accuracy_string.go b/src/math/big/accuracy_string.go
new file mode 100644 (file)
index 0000000..647a1fb
--- /dev/null
@@ -0,0 +1,16 @@
+// generated by stringer -type=Accuracy; DO NOT EDIT
+
+package big
+
+import "fmt"
+
+const _Accuracy_name = "ExactBelowAboveUndef"
+
+var _Accuracy_index = [...]uint8{0, 5, 10, 15, 20}
+
+func (i Accuracy) String() string {
+       if i < 0 || i+1 >= Accuracy(len(_Accuracy_index)) {
+               return fmt.Sprintf("Accuracy(%d)", i)
+       }
+       return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]]
+}
index de16c6eccd8a4a640ce6c1dba65ba03b5a9034c0..0ad8312afe3cad2338595c203e7d0bbf03fd7740 100644 (file)
@@ -18,15 +18,15 @@ import (
 
 const debugFloat = true // enable for debugging
 
-// A nonzero Float represents a multi-precision floating point number
+// A nonzero finite Float represents a multi-precision floating point number
 //
 //   sign × mantissa × 2**exponent
 //
 // with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp.
-// A Float may also be +0, -0, +Inf, -Inf, or NaN.
+// A Float may also be zero (+0, -0), infinite (+Inf, -Inf) or
+// not-a-number (NaN).
 //
 // Each Float value also has a precision, rounding mode, and accuracy.
-//
 // The precision is the maximum number of mantissa bits available to
 // represent the value. The rounding mode specifies how a result should
 // be rounded to fit into the mantissa bits, and accuracy describes the
@@ -46,21 +46,20 @@ const debugFloat = true // enable for debugging
 //
 // By setting the desired precision to 24 or 53 and using matching rounding
 // mode (typically ToNearestEven), Float operations produce the same results
-// as the corresponding float32 or float64 IEEE-754 arithmetic for normalized
-// operands (including +0 and -0). Exponent underflow and overflow lead to a
-// 0 or an Infinity for different values than IEEE-754 because Float exponents
-// hace a much larger range.
+// as the corresponding float32 or float64 IEEE-754 arithmetic. Exponent
+// underflow and overflow lead to a 0 or an Infinity for different values
+// than IEEE-754 because Float exponents have a much larger range.
 //
 // The zero (uninitialized) value for a Float is ready to use and represents
 // the number +0.0 exactly, with precision 0 and rounding mode ToNearestEven.
 //
 type Float struct {
+       prec uint32
        mode RoundingMode
        acc  Accuracy
        neg  bool
        mant nat
        exp  int32
-       prec uint32
 }
 
 // Internal representation: The mantissa bits x.mant of a Float x are stored
@@ -81,38 +80,10 @@ const (
        MaxPrec = math.MaxUint32    // largest (theoretically) supported precision; likely memory-limited
 )
 
-// Accuracy describes the rounding error produced by the most recent
-// operation that generated a Float value, relative to the exact value.
-// The accuracy may be Undef for operations on and resulting in
-// NaNs since they are neither Below nor Above any other value.
-type Accuracy int8
-
-// Constants describing the Accuracy of a Float.
-const (
-       Exact Accuracy = 0
-       Below Accuracy = 1 << 0
-       Above Accuracy = 1 << 1
-       Undef Accuracy = Below | Above
-)
-
-func (a Accuracy) String() string {
-       switch a {
-       case Exact:
-               return "exact"
-       case Below:
-               return "below"
-       case Above:
-               return "above"
-       case Undef:
-               return "undef"
-       }
-       panic(fmt.Sprintf("unknown accuracy %d", a))
-}
-
 // RoundingMode determines how a Float value is rounded to the
 // desired precision. Rounding may change the Float value; the
 // rounding error is described by the Float's Accuracy.
-type RoundingMode uint8
+type RoundingMode byte
 
 // The following rounding modes are supported.
 const (
@@ -124,23 +95,23 @@ const (
        ToPositiveInf                     // == IEEE 754-2008 roundTowardPositive
 )
 
-func (mode RoundingMode) String() string {
-       switch mode {
-       case ToNearestEven:
-               return "ToNearestEven"
-       case ToNearestAway:
-               return "ToNearestAway"
-       case ToZero:
-               return "ToZero"
-       case AwayFromZero:
-               return "AwayFromZero"
-       case ToNegativeInf:
-               return "ToNegativeInf"
-       case ToPositiveInf:
-               return "ToPositiveInf"
-       }
-       panic("unreachable")
-}
+//go:generate stringer -type=RoundingMode
+
+// Accuracy describes the rounding error produced by the most recent
+// operation that generated a Float value, relative to the exact value.
+// The accuracy is Undef for operations on and resulting in NaNs since
+// they are neither Below nor Above any other value.
+type Accuracy byte
+
+// Constants describing the Accuracy of a Float.
+const (
+       Exact Accuracy = 0
+       Below Accuracy = 1 << 0
+       Above Accuracy = 1 << 1
+       Undef Accuracy = Below | Above
+)
+
+//go:generate stringer -type=Accuracy
 
 // SetPrec sets z's precision to prec and returns the (possibly) rounded
 // value of z. Rounding occurs according to z's rounding mode if the mantissa
@@ -179,9 +150,10 @@ func (z *Float) SetPrec(prec uint) *Float {
 
 // SetMode sets z's rounding mode to mode and returns an exact z.
 // z remains unchanged otherwise.
+// z.SetMode(z.Mode()) is a cheap way to set z's accuracy to Exact.
 func (z *Float) SetMode(mode RoundingMode) *Float {
-       z.acc = Exact // TODO(gri) should we not do this? what's the general rule for setting accuracy?
        z.mode = mode
+       z.acc = Exact
        return z
 }
 
@@ -198,16 +170,16 @@ func (x *Float) MinPrec() uint {
        return uint(len(x.mant))*_W - x.mant.trailingZeroBits()
 }
 
-// Acc returns the accuracy of x produced by the most recent operation.
-func (x *Float) Acc() Accuracy {
-       return x.acc
-}
-
 // Mode returns the rounding mode of x.
 func (x *Float) Mode() RoundingMode {
        return x.mode
 }
 
+// Acc returns the accuracy of x produced by the most recent operation.
+func (x *Float) Acc() Accuracy {
+       return x.acc
+}
+
 // Sign returns:
 //
 //     -1 if x <   0
@@ -288,6 +260,33 @@ func (z *Float) SetMantExp(mant *Float, exp int) *Float {
        return z
 }
 
+// IsNeg reports whether x is negative.
+// A NaN is not negative.
+func (x *Float) IsNeg() bool {
+       return x.neg && x.exp != nanExp
+}
+
+// IsZero reports whether x is a +0 or -0.
+func (x *Float) IsZero() bool {
+       return len(x.mant) == 0 && x.exp == 0
+}
+
+// IsFinite reports whether -Inf < x < Inf.
+// A NaN is not finite.
+func (x *Float) IsFinite() bool {
+       return len(x.mant) != 0 || x.exp == 0
+}
+
+// IsInf reports whether x is a +Inf or -Inf.
+func (x *Float) IsInf() bool {
+       return x.exp == infExp
+}
+
+// IsNaN reports whether x is a NaN.
+func (x *Float) IsNaN() bool {
+       return x.exp == nanExp
+}
+
 // IsInt reports whether x is an integer.
 // ±Inf and NaN are not considered integers.
 func (x *Float) IsInt() bool {
@@ -303,25 +302,6 @@ func (x *Float) IsInt() bool {
        return x.prec <= uint32(x.exp) || x.MinPrec() <= uint(x.exp) // not enough bits for fractional mantissa
 }
 
-// IsInf reports whether x is an infinity, according to sign.
-// If sign > 0, IsInf reports whether x is positive infinity.
-// If sign < 0, IsInf reports whether x is negative infinity.
-// If sign == 0, IsInf reports whether x is either infinity.
-func (x *Float) IsInf(sign int) bool {
-       if debugFloat {
-               validate(x)
-       }
-       return x.exp == infExp && (sign == 0 || x.neg == (sign < 0))
-}
-
-// IsNaN reports whether x is a NaN.
-func (x *Float) IsNaN() bool {
-       if debugFloat {
-               validate(x)
-       }
-       return x.exp == nanExp
-}
-
 func (z *Float) setZero() {
        z.mant = z.mant[:0]
        z.exp = 0
@@ -715,20 +695,20 @@ func (z *Float) Set(x *Float) *Float {
        return z
 }
 
-// Copy sets z to x, with the same precision and rounding mode as x,
-// and returns z.
+// Copy sets z to x, with the same precision, rounding mode, and
+// accuracy as x, and returns z. x is not changed even if z and
+// x are the same.
 func (z *Float) Copy(x *Float) *Float {
        if debugFloat {
                validate(x)
        }
-       // TODO(gri) what about z.acc? should it be always Exact?
        if z != x {
-               z.acc = Exact
-               z.neg = x.neg
-               z.exp = x.exp
-               z.mant = z.mant.set(x.mant)
                z.prec = x.prec
                z.mode = x.mode
+               z.acc = x.acc
+               z.neg = x.neg
+               z.mant = z.mant.set(x.mant)
+               z.exp = x.exp
        }
        return z
 }
@@ -858,7 +838,7 @@ func (x *Float) Int64() (int64, Accuracy) {
 
 // Float64 returns the closest float64 value of x
 // by rounding to nearest with 53 bits precision.
-// BUG(gri) doesn't handle exponent overflow
+// BUG(gri) Float.Float64 doesn't handle exponent overflow.
 func (x *Float) Float64() (float64, Accuracy) {
        if debugFloat {
                validate(x)
@@ -955,7 +935,6 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) {
                z = new(Int)
        }
        z.neg = x.neg
-       // TODO(gri) should have a shift that takes positive and negative shift counts
        switch {
        case exp > allBits:
                z.abs = z.abs.shl(x.mant, exp-allBits)
@@ -1232,8 +1211,7 @@ func (x *Float) ucmp(y *Float) int {
        return 0
 }
 
-// Handling of sign bit as defined by IEEE 754-2008,
-// section 6.3 (note that there are no NaN Floats):
+// Handling of sign bit as defined by IEEE 754-2008, section 6.3:
 //
 // When neither the inputs nor result are NaN, the sign of a product or
 // quotient is the exclusive OR of the operands’ signs; the sign of a sum,
@@ -1252,14 +1230,13 @@ func (x *Float) ucmp(y *Float) int {
 //
 // See also: http://play.golang.org/p/RtH3UCt5IH
 
-// Add sets z to the rounded sum x+y and returns z.
-// If z's precision is 0, it is changed to the larger
-// of x's or y's precision before the operation.
-// Rounding is performed according to z's precision
-// and rounding mode; and z's accuracy reports the
-// result error relative to the exact (not rounded)
+// Add sets z to the rounded sum x+y and returns z. If z's precision is 0,
+// it is changed to the larger of x's or y's precision before the operation.
+// Rounding is performed according to z's precision and rounding mode; and
+// z's accuracy reports the result error relative to the exact (not rounded)
 // result.
-// BUG(gri) If any of the operands is Inf, the result is NaN.
+// BUG(gri) Float.Add returns NaN if an operand is Inf.
+// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect.
 func (z *Float) Add(x, y *Float) *Float {
        if debugFloat {
                validate(x)
@@ -1314,7 +1291,7 @@ func (z *Float) Add(x, y *Float) *Float {
 
 // Sub sets z to the rounded difference x-y and returns z.
 // Precision, rounding, and accuracy reporting are as for Add.
-// BUG(gri) If any of the operands is Inf, the result is NaN.
+// BUG(gri) Float.Sub returns NaN if an operand is Inf.
 func (z *Float) Sub(x, y *Float) *Float {
        if debugFloat {
                validate(x)
@@ -1369,7 +1346,7 @@ func (z *Float) Sub(x, y *Float) *Float {
 
 // Mul sets z to the rounded product x*y and returns z.
 // Precision, rounding, and accuracy reporting are as for Add.
-// BUG(gri) If any of the operands is Inf, the result is NaN.
+// BUG(gri) Float.Mul returns NaN if an operand is Inf.
 func (z *Float) Mul(x, y *Float) *Float {
        if debugFloat {
                validate(x)
@@ -1407,7 +1384,7 @@ func (z *Float) Mul(x, y *Float) *Float {
 
 // Quo sets z to the rounded quotient x/y and returns z.
 // Precision, rounding, and accuracy reporting are as for Add.
-// BUG(gri) If any of the operands is Inf, the result is NaN.
+// BUG(gri) Float.Quo returns NaN if an operand is Inf.
 func (z *Float) Quo(x, y *Float) *Float {
        if debugFloat {
                validate(x)
@@ -1454,8 +1431,7 @@ func (z *Float) Quo(x, y *Float) *Float {
 //
 // Infinities with matching sign are equal.
 // NaN values are never equal.
-// BUG(gri) comparing NaN's is not implemented
-// (should we use Accuracy here for results?)
+// BUG(gri) Float.Cmp does not implement comparing of NaNs.
 func (x *Float) Cmp(y *Float) int {
        if debugFloat {
                validate(x)
@@ -1498,7 +1474,6 @@ func umax32(x, y uint32) uint32 {
 //     +1 if 0 < x < +Inf
 //     +2 if x == +Inf
 //
-// TODO(gri) export (and remove IsInf)?
 func (x *Float) ord() int {
        m := 1 // common case
        if len(x.mant) == 0 {
index aa1cb5828b5ea94f08c81a43c2cd515279ce01ed..0be6a957d338a6d4ddbb70ee3ca9faf0ce2ecc2b 100644 (file)
@@ -78,7 +78,7 @@ func TestFloatZeroValue(t *testing.T) {
                z := make(test.z)
                test.op(z, make(test.x), make(test.y))
                got := 0
-               if !z.IsInf(0) && !z.IsNaN() {
+               if z.IsFinite() {
                        got = int(z.int64())
                }
                if got != test.want {
@@ -288,6 +288,38 @@ func TestFloatSetMantExp(t *testing.T) {
        }
 }
 
+func TestFloatPredicates(t *testing.T) {
+       for _, test := range []struct {
+               x                           string
+               neg, zero, finite, inf, nan bool
+       }{
+               {x: "-Inf", neg: true, inf: true},
+               {x: "-1", neg: true, finite: true},
+               {x: "-0", neg: true, zero: true, finite: true},
+               {x: "0", zero: true, finite: true},
+               {x: "1", finite: true},
+               {x: "+Inf", inf: true},
+               {x: "NaN", nan: true},
+       } {
+               x := makeFloat(test.x)
+               if got := x.IsNeg(); got != test.neg {
+                       t.Errorf("(%s).IsNeg() = %v; want %v", test.x, got, test.neg)
+               }
+               if got := x.IsZero(); got != test.zero {
+                       t.Errorf("(%s).IsZero() = %v; want %v", test.x, got, test.zero)
+               }
+               if got := x.IsFinite(); got != test.finite {
+                       t.Errorf("(%s).IsFinite() = %v; want %v", test.x, got, test.finite)
+               }
+               if got := x.IsInf(); got != test.inf {
+                       t.Errorf("(%s).IsInf() = %v; want %v", test.x, got, test.inf)
+               }
+               if got := x.IsNaN(); got != test.nan {
+                       t.Errorf("(%s).IsNaN() = %v; want %v", test.x, got, test.nan)
+               }
+       }
+}
+
 func TestFloatIsInt(t *testing.T) {
        for _, test := range []string{
                "0 int",
@@ -314,14 +346,6 @@ func TestFloatIsInt(t *testing.T) {
        }
 }
 
-func TestFloatIsInf(t *testing.T) {
-       // TODO(gri) implement this
-}
-
-func TestFloatIsNaN(t *testing.T) {
-       // TODO(gri) implement this
-}
-
 func fromBinary(s string) int64 {
        x, err := strconv.ParseInt(s, 2, 64)
        if err != nil {
@@ -740,10 +764,6 @@ func TestFloatSetInf(t *testing.T) {
        }
 }
 
-func TestFloatSetNaN(t *testing.T) {
-       // TODO(gri) implement
-}
-
 func TestFloatUint64(t *testing.T) {
        for _, test := range []struct {
                x   string
index 31e192f5b4e512b57aff0216dc62d61a48eb75df..23cf948a02c529bd07109e38178f79f2e3f9008a 100644 (file)
@@ -63,7 +63,7 @@ func (z *Float) SetString(s string) (*Float, bool) {
 // be binary, if present (an "e" or "E" exponent indicator cannot be
 // distinguished from a mantissa digit).
 //
-// BUG(gri) This signature conflicts with Scan(s fmt.ScanState, ch rune) error.
+// BUG(gri) The Float.Scan signature conflicts with Scan(s fmt.ScanState, ch rune) error.
 func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
        if z.prec == 0 {
                z.prec = 64
@@ -224,7 +224,7 @@ func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b i
 // number of digits necessary such that ParseFloat will return f exactly.
 // The prec value is ignored for the 'b' or 'p' format.
 //
-// BUG(gri) Currently, Format does not accept negative precisions.
+// BUG(gri) Float.Format does not accept negative precisions.
 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))
@@ -236,7 +236,7 @@ func (x *Float) Append(buf []byte, format byte, prec int) []byte {
        // TODO(gri) factor out handling of sign?
 
        // Inf
-       if x.IsInf(0) {
+       if x.IsInf() {
                var ch byte = '+'
                if x.neg {
                        ch = '-'
@@ -261,7 +261,7 @@ func (x *Float) Append(buf []byte, format byte, prec int) []byte {
        return x.bigFtoa(buf, format, prec)
 }
 
-// BUG(gri): Currently, String uses x.Format('g', 10) rather than x.Format('g', -1).
+// 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)
 }
index 460ef9c0a65fadf753f56f15d70dd2790839cb7b..23655d09660f7189478cc715d7d410a3d33a80fd 100644 (file)
@@ -12,7 +12,7 @@ import (
 // TODO(gri) add more examples
 
 func ExampleFloat_Add() {
-       // Operating on numbers of different precision is easy.
+       // Operating on numbers of different precision.
        var x, y, z big.Float
        x.SetInt64(1000)          // x is automatically set to 64bit precision
        y.SetFloat64(2.718281828) // y is automatically set to 53bit precision
@@ -22,9 +22,9 @@ func ExampleFloat_Add() {
        fmt.Printf("y = %s (%s, prec = %d, acc = %s)\n", &y, y.Format('p', 0), y.Prec(), y.Acc())
        fmt.Printf("z = %s (%s, prec = %d, acc = %s)\n", &z, z.Format('p', 0), z.Prec(), z.Acc())
        // Output:
-       // x = 1000 (0x.fap10, prec = 64, acc = exact)
-       // y = 2.718281828 (0x.adf85458248cd8p2, prec = 53, acc = exact)
-       // z = 1002.718282 (0x.faadf854p10, prec = 32, acc = below)
+       // x = 1000 (0x.fap10, prec = 64, acc = Exact)
+       // y = 2.718281828 (0x.adf85458248cd8p2, prec = 53, acc = Exact)
+       // z = 1002.718282 (0x.faadf854p10, prec = 32, acc = Below)
 }
 
 func Example_Shift() {
diff --git a/src/math/big/roundingmode_string.go b/src/math/big/roundingmode_string.go
new file mode 100644 (file)
index 0000000..05024b8
--- /dev/null
@@ -0,0 +1,16 @@
+// generated by stringer -type=RoundingMode; DO NOT EDIT
+
+package big
+
+import "fmt"
+
+const _RoundingMode_name = "ToNearestEvenToNearestAwayToZeroAwayFromZeroToNegativeInfToPositiveInf"
+
+var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70}
+
+func (i RoundingMode) String() string {
+       if i+1 >= RoundingMode(len(_RoundingMode_index)) {
+               return fmt.Sprintf("RoundingMode(%d)", i)
+       }
+       return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]]
+}