1
0
mirror of https://github.com/golang/go synced 2024-11-19 11:24:51 -07:00

math/big: use stringer for enum String() methods

Change-Id: Ide0615542d67b7d81bf6c56aab550e142a8789f7
Reviewed-on: https://go-review.googlesource.com/6682
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Robert Griesemer 2015-03-03 14:17:39 -08:00
parent 0a8a625848
commit 2a1728d009
6 changed files with 151 additions and 124 deletions

View File

@ -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]]
}

View File

@ -18,15 +18,15 @@ import (
const debugFloat = true // enable for debugging 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 // sign × mantissa × 2**exponent
// //
// with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp. // 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. // Each Float value also has a precision, rounding mode, and accuracy.
//
// The precision is the maximum number of mantissa bits available to // The precision is the maximum number of mantissa bits available to
// represent the value. The rounding mode specifies how a result should // represent the value. The rounding mode specifies how a result should
// be rounded to fit into the mantissa bits, and accuracy describes the // 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 // By setting the desired precision to 24 or 53 and using matching rounding
// mode (typically ToNearestEven), Float operations produce the same results // mode (typically ToNearestEven), Float operations produce the same results
// as the corresponding float32 or float64 IEEE-754 arithmetic for normalized // as the corresponding float32 or float64 IEEE-754 arithmetic. Exponent
// operands (including +0 and -0). Exponent underflow and overflow lead to a // underflow and overflow lead to a 0 or an Infinity for different values
// 0 or an Infinity for different values than IEEE-754 because Float exponents // than IEEE-754 because Float exponents have a much larger range.
// hace a much larger range.
// //
// The zero (uninitialized) value for a Float is ready to use and represents // 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. // the number +0.0 exactly, with precision 0 and rounding mode ToNearestEven.
// //
type Float struct { type Float struct {
prec uint32
mode RoundingMode mode RoundingMode
acc Accuracy acc Accuracy
neg bool neg bool
mant nat mant nat
exp int32 exp int32
prec uint32
} }
// Internal representation: The mantissa bits x.mant of a Float x are stored // 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 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 // RoundingMode determines how a Float value is rounded to the
// desired precision. Rounding may change the Float value; the // desired precision. Rounding may change the Float value; the
// rounding error is described by the Float's Accuracy. // rounding error is described by the Float's Accuracy.
type RoundingMode uint8 type RoundingMode byte
// The following rounding modes are supported. // The following rounding modes are supported.
const ( const (
@ -124,23 +95,23 @@ const (
ToPositiveInf // == IEEE 754-2008 roundTowardPositive ToPositiveInf // == IEEE 754-2008 roundTowardPositive
) )
func (mode RoundingMode) String() string { //go:generate stringer -type=RoundingMode
switch mode {
case ToNearestEven: // Accuracy describes the rounding error produced by the most recent
return "ToNearestEven" // operation that generated a Float value, relative to the exact value.
case ToNearestAway: // The accuracy is Undef for operations on and resulting in NaNs since
return "ToNearestAway" // they are neither Below nor Above any other value.
case ToZero: type Accuracy byte
return "ToZero"
case AwayFromZero: // Constants describing the Accuracy of a Float.
return "AwayFromZero" const (
case ToNegativeInf: Exact Accuracy = 0
return "ToNegativeInf" Below Accuracy = 1 << 0
case ToPositiveInf: Above Accuracy = 1 << 1
return "ToPositiveInf" Undef Accuracy = Below | Above
} )
panic("unreachable")
} //go:generate stringer -type=Accuracy
// SetPrec sets z's precision to prec and returns the (possibly) rounded // 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 // 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. // SetMode sets z's rounding mode to mode and returns an exact z.
// z remains unchanged otherwise. // 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 { 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.mode = mode
z.acc = Exact
return z return z
} }
@ -198,16 +170,16 @@ func (x *Float) MinPrec() uint {
return uint(len(x.mant))*_W - x.mant.trailingZeroBits() 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. // Mode returns the rounding mode of x.
func (x *Float) Mode() RoundingMode { func (x *Float) Mode() RoundingMode {
return x.mode 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: // Sign returns:
// //
// -1 if x < 0 // -1 if x < 0
@ -288,6 +260,33 @@ func (z *Float) SetMantExp(mant *Float, exp int) *Float {
return z 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. // IsInt reports whether x is an integer.
// ±Inf and NaN are not considered integers. // ±Inf and NaN are not considered integers.
func (x *Float) IsInt() bool { 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 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() { func (z *Float) setZero() {
z.mant = z.mant[:0] z.mant = z.mant[:0]
z.exp = 0 z.exp = 0
@ -715,20 +695,20 @@ func (z *Float) Set(x *Float) *Float {
return z return z
} }
// Copy sets z to x, with the same precision and rounding mode as x, // Copy sets z to x, with the same precision, rounding mode, and
// and returns z. // 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 { func (z *Float) Copy(x *Float) *Float {
if debugFloat { if debugFloat {
validate(x) validate(x)
} }
// TODO(gri) what about z.acc? should it be always Exact?
if z != x { 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.prec = x.prec
z.mode = x.mode 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 return z
} }
@ -858,7 +838,7 @@ func (x *Float) Int64() (int64, Accuracy) {
// Float64 returns the closest float64 value of x // Float64 returns the closest float64 value of x
// by rounding to nearest with 53 bits precision. // 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) { func (x *Float) Float64() (float64, Accuracy) {
if debugFloat { if debugFloat {
validate(x) validate(x)
@ -955,7 +935,6 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) {
z = new(Int) z = new(Int)
} }
z.neg = x.neg z.neg = x.neg
// TODO(gri) should have a shift that takes positive and negative shift counts
switch { switch {
case exp > allBits: case exp > allBits:
z.abs = z.abs.shl(x.mant, exp-allBits) z.abs = z.abs.shl(x.mant, exp-allBits)
@ -1232,8 +1211,7 @@ func (x *Float) ucmp(y *Float) int {
return 0 return 0
} }
// Handling of sign bit as defined by IEEE 754-2008, // Handling of sign bit as defined by IEEE 754-2008, section 6.3:
// section 6.3 (note that there are no NaN Floats):
// //
// When neither the inputs nor result are NaN, the sign of a product or // 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, // 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 // See also: http://play.golang.org/p/RtH3UCt5IH
// Add sets z to the rounded sum x+y and returns z. // Add sets z to the rounded sum x+y and returns z. If z's precision is 0,
// If z's precision is 0, it is changed to the larger // it is changed to the larger of x's or y's precision before the operation.
// of x's or y's precision before the operation. // Rounding is performed according to z's precision and rounding mode; and
// Rounding is performed according to z's precision // z's accuracy reports the result error relative to the exact (not rounded)
// and rounding mode; and z's accuracy reports the
// result error relative to the exact (not rounded)
// result. // 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 { func (z *Float) Add(x, y *Float) *Float {
if debugFloat { if debugFloat {
validate(x) 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. // Sub sets z to the rounded difference x-y and returns z.
// Precision, rounding, and accuracy reporting are as for Add. // 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 { func (z *Float) Sub(x, y *Float) *Float {
if debugFloat { if debugFloat {
validate(x) 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. // Mul sets z to the rounded product x*y and returns z.
// Precision, rounding, and accuracy reporting are as for Add. // 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 { func (z *Float) Mul(x, y *Float) *Float {
if debugFloat { if debugFloat {
validate(x) 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. // Quo sets z to the rounded quotient x/y and returns z.
// Precision, rounding, and accuracy reporting are as for Add. // 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 { func (z *Float) Quo(x, y *Float) *Float {
if debugFloat { if debugFloat {
validate(x) validate(x)
@ -1454,8 +1431,7 @@ func (z *Float) Quo(x, y *Float) *Float {
// //
// Infinities with matching sign are equal. // Infinities with matching sign are equal.
// NaN values are never equal. // NaN values are never equal.
// BUG(gri) comparing NaN's is not implemented // BUG(gri) Float.Cmp does not implement comparing of NaNs.
// (should we use Accuracy here for results?)
func (x *Float) Cmp(y *Float) int { func (x *Float) Cmp(y *Float) int {
if debugFloat { if debugFloat {
validate(x) validate(x)
@ -1498,7 +1474,6 @@ func umax32(x, y uint32) uint32 {
// +1 if 0 < x < +Inf // +1 if 0 < x < +Inf
// +2 if x == +Inf // +2 if x == +Inf
// //
// TODO(gri) export (and remove IsInf)?
func (x *Float) ord() int { func (x *Float) ord() int {
m := 1 // common case m := 1 // common case
if len(x.mant) == 0 { if len(x.mant) == 0 {

View File

@ -78,7 +78,7 @@ func TestFloatZeroValue(t *testing.T) {
z := make(test.z) z := make(test.z)
test.op(z, make(test.x), make(test.y)) test.op(z, make(test.x), make(test.y))
got := 0 got := 0
if !z.IsInf(0) && !z.IsNaN() { if z.IsFinite() {
got = int(z.int64()) got = int(z.int64())
} }
if got != test.want { 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) { func TestFloatIsInt(t *testing.T) {
for _, test := range []string{ for _, test := range []string{
"0 int", "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 { func fromBinary(s string) int64 {
x, err := strconv.ParseInt(s, 2, 64) x, err := strconv.ParseInt(s, 2, 64)
if err != nil { 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) { func TestFloatUint64(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
x string x string

View 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 // be binary, if present (an "e" or "E" exponent indicator cannot be
// distinguished from a mantissa digit). // 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) { func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
if z.prec == 0 { if z.prec == 0 {
z.prec = 64 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. // number of digits necessary such that ParseFloat will return f exactly.
// The prec value is ignored for the 'b' or 'p' format. // 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 { func (x *Float) Format(format byte, prec int) string {
const extra = 10 // TODO(gri) determine a good/better value here const extra = 10 // TODO(gri) determine a good/better value here
return string(x.Append(make([]byte, 0, prec+extra), format, prec)) 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? // TODO(gri) factor out handling of sign?
// Inf // Inf
if x.IsInf(0) { if x.IsInf() {
var ch byte = '+' var ch byte = '+'
if x.neg { if x.neg {
ch = '-' ch = '-'
@ -261,7 +261,7 @@ func (x *Float) Append(buf []byte, format byte, prec int) []byte {
return x.bigFtoa(buf, format, prec) 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 { func (x *Float) String() string {
return x.Format('g', 10) return x.Format('g', 10)
} }

View File

@ -12,7 +12,7 @@ import (
// TODO(gri) add more examples // TODO(gri) add more examples
func ExampleFloat_Add() { func ExampleFloat_Add() {
// Operating on numbers of different precision is easy. // Operating on numbers of different precision.
var x, y, z big.Float var x, y, z big.Float
x.SetInt64(1000) // x is automatically set to 64bit precision x.SetInt64(1000) // x is automatically set to 64bit precision
y.SetFloat64(2.718281828) // y is automatically set to 53bit 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("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()) fmt.Printf("z = %s (%s, prec = %d, acc = %s)\n", &z, z.Format('p', 0), z.Prec(), z.Acc())
// Output: // Output:
// x = 1000 (0x.fap10, prec = 64, acc = exact) // x = 1000 (0x.fap10, prec = 64, acc = Exact)
// y = 2.718281828 (0x.adf85458248cd8p2, prec = 53, acc = exact) // y = 2.718281828 (0x.adf85458248cd8p2, prec = 53, acc = Exact)
// z = 1002.718282 (0x.faadf854p10, prec = 32, acc = below) // z = 1002.718282 (0x.faadf854p10, prec = 32, acc = Below)
} }
func Example_Shift() { func Example_Shift() {

View File

@ -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]]
}