Commit fa85a720 authored by Robert Griesemer's avatar Robert Griesemer

math/big: remove NaN support - just not worth it

NaNs make the API more complicated for no real good reasons.
There are few operations that produce NaNs with IEEE arithmetic,
there's no need to copy the behavior. It's easy to test for these
scenarios and avoid them (on the other hand, it's not easy to test
for overflow or underflow, so we want to keep +/-Inf).

Also:
- renamed IsNeg -> Signbit (clearer, especially for x == -0)
- removed IsZero           (Sign() == 0 is sufficient and efficient)
- removed IsFinite         (now same as !IsInf)

Change-Id: I3f3b4445c325d9bbb1bf46ce2e298a6aeb498e07
Reviewed-on: https://go-review.googlesource.com/8280Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent 67426a8a
...@@ -4,13 +4,14 @@ package big ...@@ -4,13 +4,14 @@ package big
import "fmt" import "fmt"
const _Accuracy_name = "ExactBelowAboveUndef" const _Accuracy_name = "BelowExactAbove"
var _Accuracy_index = [...]uint8{0, 5, 10, 15, 20} var _Accuracy_index = [...]uint8{0, 5, 10, 15}
func (i Accuracy) String() string { func (i Accuracy) String() string {
i -= -1
if i < 0 || i+1 >= Accuracy(len(_Accuracy_index)) { if i < 0 || i+1 >= Accuracy(len(_Accuracy_index)) {
return fmt.Sprintf("Accuracy(%d)", i) return fmt.Sprintf("Accuracy(%d)", i+-1)
} }
return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]] return _Accuracy_name[_Accuracy_index[i]:_Accuracy_index[i+1]]
} }
...@@ -23,10 +23,9 @@ const debugFloat = true // enable for debugging ...@@ -23,10 +23,9 @@ const debugFloat = true // enable for debugging
// 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 zero (+0, -0), infinite (+Inf, -Inf) or // A Float may also be zero (+0, -0) or infinite (+Inf, -Inf).
// not-a-number (NaN). Except for NaNs, all Floats are ordered, // All Floats are ordered, and the ordering of two Floats x and y
// and the ordering of two Floats x and y is defined by x.Cmp(y). // is defined by x.Cmp(y).
// NaNs are always different from any other Float value.
// //
// 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
...@@ -34,10 +33,10 @@ const debugFloat = true // enable for debugging ...@@ -34,10 +33,10 @@ const debugFloat = true // enable for debugging
// be rounded to fit into the mantissa bits, and accuracy describes the // be rounded to fit into the mantissa bits, and accuracy describes the
// rounding error with respect to the exact result. // rounding error with respect to the exact result.
// //
// All operations, including setters, that specify a *Float variable for // Unless specified otherwise, all operations (including setters) that
// the result (usually via the receiver with the exception of MantExp), // specify a *Float variable for the result (usually via the receiver
// round the numeric result according to the precision and rounding mode // with the exception of MantExp), round the numeric result according
// of the result variable, unless specified otherwise. // to the precision and rounding mode of the result variable.
// //
// If the provided result precision is 0 (see below), it is set to the // If the provided result precision is 0 (see below), it is set to the
// precision of the argument with the largest precision value before any // precision of the argument with the largest precision value before any
...@@ -48,9 +47,10 @@ const debugFloat = true // enable for debugging ...@@ -48,9 +47,10 @@ 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. Exponent // as the corresponding float32 or float64 IEEE-754 arithmetic for operands
// underflow and overflow lead to a 0 or an Infinity for different values // that correspond to normal (i.e., not denormal) float32 or float64 numbers.
// than IEEE-754 because Float exponents have a much larger range. // 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 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.
...@@ -65,9 +65,19 @@ type Float struct { ...@@ -65,9 +65,19 @@ type Float struct {
exp int32 exp int32
} }
// Float operations that would lead to a NaN under IEEE-754 rules cause
// a run-time panic of ErrNaN type.
type ErrNaN struct {
msg string
}
// NewFloat allocates and returns a new Float set to x, // NewFloat allocates and returns a new Float set to x,
// with precision 53 and rounding mode ToNearestEven. // with precision 53 and rounding mode ToNearestEven.
// NewFloat panics with ErrNan if x is a NaN.
func NewFloat(x float64) *Float { func NewFloat(x float64) *Float {
if math.IsNaN(x) {
panic(ErrNaN{"NewFloat(NaN)"})
}
return new(Float).SetFloat64(x) return new(Float).SetFloat64(x)
} }
...@@ -87,15 +97,13 @@ const ( ...@@ -87,15 +97,13 @@ const (
// x.mant[0] has trailing zero bits. The msb of the mantissa corresponds // x.mant[0] has trailing zero bits. The msb of the mantissa corresponds
// to the value 0.5; the exponent x.exp shifts the binary point as needed. // to the value 0.5; the exponent x.exp shifts the binary point as needed.
// //
// A zero or non-finite Float x ignores x.mant and x.exp. A NaN x ignores // A zero or non-finite Float x ignores x.mant and x.exp.
// the sign x.neg.
// //
// x form neg mant exp // x form neg mant exp
// ---------------------------------------------------------- // ----------------------------------------------------------
// ±0 zero sign - - // ±0 zero sign - -
// 0 < |x| < +Inf finite sign mantissa exponent // 0 < |x| < +Inf finite sign mantissa exponent
// ±Inf inf sign - - // ±Inf inf sign - -
// NaN nan - - -
// A form value describes the internal representation. // A form value describes the internal representation.
type form byte type form byte
...@@ -105,7 +113,6 @@ const ( ...@@ -105,7 +113,6 @@ const (
zero form = iota zero form = iota
finite finite
inf inf
nan
) )
// RoundingMode determines how a Float value is rounded to the // RoundingMode determines how a Float value is rounded to the
...@@ -127,16 +134,13 @@ const ( ...@@ -127,16 +134,13 @@ const (
// Accuracy describes the rounding error produced by the most recent // Accuracy describes the rounding error produced by the most recent
// operation that generated a Float value, relative to the exact value. // operation that generated a Float value, relative to the exact value.
// The accuracy is Undef for operations on and resulting in NaNs since type Accuracy int8
// they are neither Below nor Above any other value.
type Accuracy byte
// Constants describing the Accuracy of a Float. // Constants describing the Accuracy of a Float.
const ( const (
Below Accuracy = -1
Exact Accuracy = 0 Exact Accuracy = 0
Below Accuracy = 1 << 0 Above Accuracy = +1
Above Accuracy = 1 << 1
Undef Accuracy = Below | Above
) )
//go:generate stringer -type=Accuracy //go:generate stringer -type=Accuracy
...@@ -144,8 +148,8 @@ const ( ...@@ -144,8 +148,8 @@ const (
// 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
// cannot be represented in prec bits without loss of precision. // cannot be represented in prec bits without loss of precision.
// SetPrec(0) maps all finite values to ±0; infinite and NaN values remain // SetPrec(0) maps all finite values to ±0; infinite values remain unchanged.
// unchanged. If prec > MaxPrec, it is set to MaxPrec. // If prec > MaxPrec, it is set to MaxPrec.
func (z *Float) SetPrec(prec uint) *Float { func (z *Float) SetPrec(prec uint) *Float {
z.acc = Exact // optimistically assume no rounding is needed z.acc = Exact // optimistically assume no rounding is needed
...@@ -189,14 +193,14 @@ func (z *Float) SetMode(mode RoundingMode) *Float { ...@@ -189,14 +193,14 @@ func (z *Float) SetMode(mode RoundingMode) *Float {
} }
// Prec returns the mantissa precision of x in bits. // Prec returns the mantissa precision of x in bits.
// The result may be 0 for |x| == 0, |x| == Inf, or NaN. // The result may be 0 for |x| == 0 and |x| == Inf.
func (x *Float) Prec() uint { func (x *Float) Prec() uint {
return uint(x.prec) return uint(x.prec)
} }
// MinPrec returns the minimum precision required to represent x exactly // MinPrec returns the minimum precision required to represent x exactly
// (i.e., the smallest prec before x.SetPrec(prec) would start rounding x). // (i.e., the smallest prec before x.SetPrec(prec) would start rounding x).
// The result is 0 if x is 0 or not finite. // The result is 0 for |x| == 0 and |x| == Inf.
func (x *Float) MinPrec() uint { func (x *Float) MinPrec() uint {
if x.form != finite { if x.form != finite {
return 0 return 0
...@@ -217,14 +221,14 @@ func (x *Float) Acc() Accuracy { ...@@ -217,14 +221,14 @@ func (x *Float) Acc() Accuracy {
// Sign returns: // Sign returns:
// //
// -1 if x < 0 // -1 if x < 0
// 0 if x is ±0 or NaN // 0 if x is ±0
// +1 if x > 0 // +1 if x > 0
// //
func (x *Float) Sign() int { func (x *Float) Sign() int {
if debugFloat { if debugFloat {
x.validate() x.validate()
} }
if x.form == zero || x.form == nan { if x.form == zero {
return 0 return 0
} }
if x.neg { if x.neg {
...@@ -245,7 +249,6 @@ func (x *Float) Sign() int { ...@@ -245,7 +249,6 @@ func (x *Float) Sign() int {
// //
// ( ±0).MantExp(mant) = 0, with mant set to ±0 // ( ±0).MantExp(mant) = 0, with mant set to ±0
// (±Inf).MantExp(mant) = 0, with mant set to ±Inf // (±Inf).MantExp(mant) = 0, with mant set to ±Inf
// ( NaN).MantExp(mant) = 0, with mant set to NaN
// //
// x and mant may be the same in which case x is set to its // x and mant may be the same in which case x is set to its
// mantissa value. // mantissa value.
...@@ -297,7 +300,6 @@ func (z *Float) setExpAndRound(exp int64, sbit uint) { ...@@ -297,7 +300,6 @@ func (z *Float) setExpAndRound(exp int64, sbit uint) {
// //
// z.SetMantExp( ±0, exp) = ±0 // z.SetMantExp( ±0, exp) = ±0
// z.SetMantExp(±Inf, exp) = ±Inf // z.SetMantExp(±Inf, exp) = ±Inf
// z.SetMantExp( NaN, exp) = NaN
// //
// z and mant may be the same in which case z's exponent // z and mant may be the same in which case z's exponent
// is set to exp. // is set to exp.
...@@ -314,21 +316,9 @@ func (z *Float) SetMantExp(mant *Float, exp int) *Float { ...@@ -314,21 +316,9 @@ func (z *Float) SetMantExp(mant *Float, exp int) *Float {
return z return z
} }
// IsNeg reports whether x is negative. // Signbit returns true if x is negative or negative zero.
// A NaN value is not negative. func (x *Float) Signbit() bool {
func (x *Float) IsNeg() bool { return x.neg
return x.neg && x.form != nan
}
// IsZero reports whether x is +0 or -0.
func (x *Float) IsZero() bool {
return x.form == zero
}
// IsFinite reports whether -Inf < x < Inf.
// A NaN value is not finite.
func (x *Float) IsFinite() bool {
return x.form <= finite
} }
// IsInf reports whether x is +Inf or -Inf. // IsInf reports whether x is +Inf or -Inf.
...@@ -336,13 +326,8 @@ func (x *Float) IsInf() bool { ...@@ -336,13 +326,8 @@ func (x *Float) IsInf() bool {
return x.form == inf return x.form == inf
} }
// IsNaN reports whether x is a NaN value.
func (x *Float) IsNaN() bool {
return x.form == nan
}
// IsInt reports whether x is an integer. // IsInt reports whether x is an integer.
// ±Inf and NaN values are not integers. // ±Inf values are not integers.
func (x *Float) IsInt() bool { func (x *Float) IsInt() bool {
if debugFloat { if debugFloat {
x.validate() x.validate()
...@@ -526,7 +511,7 @@ func (z *Float) round(sbit uint) { ...@@ -526,7 +511,7 @@ func (z *Float) round(sbit uint) {
// update accuracy // update accuracy
if z.acc != Exact && z.neg { if z.acc != Exact && z.neg {
z.acc ^= Below | Above z.acc = -z.acc
} }
if debugFloat { if debugFloat {
...@@ -598,13 +583,13 @@ func (z *Float) SetInt64(x int64) *Float { ...@@ -598,13 +583,13 @@ func (z *Float) SetInt64(x int64) *Float {
// SetFloat64 sets z to the (possibly rounded) value of x and returns z. // SetFloat64 sets z to the (possibly rounded) value of x and returns z.
// If z's precision is 0, it is changed to 53 (and rounding will have // If z's precision is 0, it is changed to 53 (and rounding will have
// no effect). // no effect). SetFloat64 panics with ErrNaN if x is a NaN.
func (z *Float) SetFloat64(x float64) *Float { func (z *Float) SetFloat64(x float64) *Float {
if z.prec == 0 { if z.prec == 0 {
z.prec = 53 z.prec = 53
} }
if math.IsNaN(x) { if math.IsNaN(x) {
return z.SetNaN() panic(ErrNaN{"Float.SetFloat64(NaN)"})
} }
z.acc = Exact z.acc = Exact
z.neg = math.Signbit(x) // handle -0, -Inf correctly z.neg = math.Signbit(x) // handle -0, -Inf correctly
...@@ -684,21 +669,14 @@ func (z *Float) SetRat(x *Rat) *Float { ...@@ -684,21 +669,14 @@ func (z *Float) SetRat(x *Rat) *Float {
return z.Quo(&a, &b) return z.Quo(&a, &b)
} }
// SetInf sets z to the infinite Float +Inf for sign >= 0, // SetInf sets z to the infinite Float -Inf if signbit is
// or -Inf for sign < 0, and returns z. The precision of // set, or +Inf if signbit is not set, and returns z. The
// z is unchanged and the result is always Exact. // precision of z is unchanged and the result is always
func (z *Float) SetInf(sign int) *Float { // Exact.
func (z *Float) SetInf(signbit bool) *Float {
z.acc = Exact z.acc = Exact
z.form = inf z.form = inf
z.neg = sign < 0 z.neg = signbit
return z
}
// SetNaN sets z to a NaN value, and returns z.
// The precision of z is unchanged and the result accuracy is always Undef.
func (z *Float) SetNaN() *Float {
z.acc = Undef
z.form = nan
return z return z
} }
...@@ -774,8 +752,8 @@ func high64(x nat) uint64 { ...@@ -774,8 +752,8 @@ func high64(x nat) uint64 {
// Uint64 returns the unsigned integer resulting from truncating x // Uint64 returns the unsigned integer resulting from truncating x
// towards zero. If 0 <= x <= math.MaxUint64, the result is Exact // towards zero. If 0 <= x <= math.MaxUint64, the result is Exact
// if x is an integer and Below otherwise. // if x is an integer and Below otherwise.
// The result is (0, Above) for x < 0, (math.MaxUint64, Below) // The result is (0, Above) for x < 0, and (math.MaxUint64, Below)
// for x > math.MaxUint64, and (0, Undef) for NaNs. // for x > math.MaxUint64.
func (x *Float) Uint64() (uint64, Accuracy) { func (x *Float) Uint64() (uint64, Accuracy) {
if debugFloat { if debugFloat {
x.validate() x.validate()
...@@ -811,9 +789,6 @@ func (x *Float) Uint64() (uint64, Accuracy) { ...@@ -811,9 +789,6 @@ func (x *Float) Uint64() (uint64, Accuracy) {
return 0, Above return 0, Above
} }
return math.MaxUint64, Below return math.MaxUint64, Below
case nan:
return 0, Undef
} }
panic("unreachable") panic("unreachable")
...@@ -823,7 +798,7 @@ func (x *Float) Uint64() (uint64, Accuracy) { ...@@ -823,7 +798,7 @@ func (x *Float) Uint64() (uint64, Accuracy) {
// If math.MinInt64 <= x <= math.MaxInt64, the result is Exact if x is // If math.MinInt64 <= x <= math.MaxInt64, the result is Exact if x is
// an integer, and Above (x < 0) or Below (x > 0) otherwise. // an integer, and Above (x < 0) or Below (x > 0) otherwise.
// The result is (math.MinInt64, Above) for x < math.MinInt64, // The result is (math.MinInt64, Above) for x < math.MinInt64,
// (math.MaxInt64, Below) for x > math.MaxInt64, and (0, Undef) for NaNs. // and (math.MaxInt64, Below) for x > math.MaxInt64.
func (x *Float) Int64() (int64, Accuracy) { func (x *Float) Int64() (int64, Accuracy) {
if debugFloat { if debugFloat {
x.validate() x.validate()
...@@ -869,9 +844,6 @@ func (x *Float) Int64() (int64, Accuracy) { ...@@ -869,9 +844,6 @@ func (x *Float) Int64() (int64, Accuracy) {
return math.MinInt64, Above return math.MinInt64, Above
} }
return math.MaxInt64, Below return math.MaxInt64, Below
case nan:
return 0, Undef
} }
panic("unreachable") panic("unreachable")
...@@ -885,7 +857,6 @@ func (x *Float) Int64() (int64, Accuracy) { ...@@ -885,7 +857,6 @@ func (x *Float) Int64() (int64, Accuracy) {
// is (0, Below) or (-0, Above), respectively, depending on the sign of x. // is (0, Below) or (-0, Above), respectively, depending on the sign of x.
// If x is too large to be represented by a float32 (|x| > math.MaxFloat32), // If x is too large to be represented by a float32 (|x| > math.MaxFloat32),
// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x. // the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x.
// The result is (NaN, Undef) for NaNs.
func (x *Float) Float32() (float32, Accuracy) { func (x *Float) Float32() (float32, Accuracy) {
if debugFloat { if debugFloat {
x.validate() x.validate()
...@@ -976,9 +947,6 @@ func (x *Float) Float32() (float32, Accuracy) { ...@@ -976,9 +947,6 @@ func (x *Float) Float32() (float32, Accuracy) {
return float32(math.Inf(-1)), Exact return float32(math.Inf(-1)), Exact
} }
return float32(math.Inf(+1)), Exact return float32(math.Inf(+1)), Exact
case nan:
return float32(math.NaN()), Undef
} }
panic("unreachable") panic("unreachable")
...@@ -989,7 +957,6 @@ func (x *Float) Float32() (float32, Accuracy) { ...@@ -989,7 +957,6 @@ func (x *Float) Float32() (float32, Accuracy) {
// is (0, Below) or (-0, Above), respectively, depending on the sign of x. // is (0, Below) or (-0, Above), respectively, depending on the sign of x.
// If x is too large to be represented by a float64 (|x| > math.MaxFloat64), // If x is too large to be represented by a float64 (|x| > math.MaxFloat64),
// the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x. // the result is (+Inf, Above) or (-Inf, Below), depending on the sign of x.
// The result is (NaN, Undef) for NaNs.
func (x *Float) Float64() (float64, Accuracy) { func (x *Float) Float64() (float64, Accuracy) {
if debugFloat { if debugFloat {
x.validate() x.validate()
...@@ -1080,16 +1047,13 @@ func (x *Float) Float64() (float64, Accuracy) { ...@@ -1080,16 +1047,13 @@ func (x *Float) Float64() (float64, Accuracy) {
return math.Inf(-1), Exact return math.Inf(-1), Exact
} }
return math.Inf(+1), Exact return math.Inf(+1), Exact
case nan:
return math.NaN(), Undef
} }
panic("unreachable") panic("unreachable")
} }
// Int returns the result of truncating x towards zero; // Int returns the result of truncating x towards zero;
// or nil if x is an infinity or NaN. // or nil if x is an infinity.
// The result is Exact if x.IsInt(); otherwise it is Below // The result is Exact if x.IsInt(); otherwise it is Below
// for x > 0, and Above for x < 0. // for x > 0, and Above for x < 0.
// If a non-nil *Int argument z is provided, Int stores // If a non-nil *Int argument z is provided, Int stores
...@@ -1140,17 +1104,14 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) { ...@@ -1140,17 +1104,14 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) {
case inf: case inf:
return nil, makeAcc(x.neg) return nil, makeAcc(x.neg)
case nan:
return nil, Undef
} }
panic("unreachable") panic("unreachable")
} }
// Rat returns the rational number corresponding to x; // Rat returns the rational number corresponding to x;
// or nil if x is an infinity or NaN. // or nil if x is an infinity.
// The result is Exact is x is not an Inf or NaN. // The result is Exact is x is not an Inf.
// If a non-nil *Rat argument z is provided, Rat stores // If a non-nil *Rat argument z is provided, Rat stores
// the result in z instead of allocating a new Rat. // the result in z instead of allocating a new Rat.
func (x *Float) Rat(z *Rat) (*Rat, Accuracy) { func (x *Float) Rat(z *Rat) (*Rat, Accuracy) {
...@@ -1190,9 +1151,6 @@ func (x *Float) Rat(z *Rat) (*Rat, Accuracy) { ...@@ -1190,9 +1151,6 @@ func (x *Float) Rat(z *Rat) (*Rat, Accuracy) {
case inf: case inf:
return nil, makeAcc(x.neg) return nil, makeAcc(x.neg)
case nan:
return nil, Undef
} }
panic("unreachable") panic("unreachable")
...@@ -1379,19 +1337,19 @@ func (z *Float) uquo(x, y *Float) { ...@@ -1379,19 +1337,19 @@ func (z *Float) uquo(x, y *Float) {
z.setExpAndRound(e-fnorm(z.mant), sbit) z.setExpAndRound(e-fnorm(z.mant), sbit)
} }
// ucmp returns Below, Exact, or Above, depending // ucmp returns -1, 0, or +1, depending on whether
// on whether |x| < |y|, |x| == |y|, or |x| > |y|. // |x| < |y|, |x| == |y|, or |x| > |y|.
// x and y must have a non-empty mantissa and valid exponent. // x and y must have a non-empty mantissa and valid exponent.
func (x *Float) ucmp(y *Float) Accuracy { func (x *Float) ucmp(y *Float) int {
if debugFloat { if debugFloat {
validateBinaryOperands(x, y) validateBinaryOperands(x, y)
} }
switch { switch {
case x.exp < y.exp: case x.exp < y.exp:
return Below return -1
case x.exp > y.exp: case x.exp > y.exp:
return Above return +1
} }
// x.exp == y.exp // x.exp == y.exp
...@@ -1410,13 +1368,13 @@ func (x *Float) ucmp(y *Float) Accuracy { ...@@ -1410,13 +1368,13 @@ func (x *Float) ucmp(y *Float) Accuracy {
} }
switch { switch {
case xm < ym: case xm < ym:
return Below return -1
case xm > ym: case xm > ym:
return Above return +1
} }
} }
return Exact return 0
} }
// Handling of sign bit as defined by IEEE 754-2008, section 6.3: // Handling of sign bit as defined by IEEE 754-2008, section 6.3:
...@@ -1443,7 +1401,7 @@ func (x *Float) ucmp(y *Float) Accuracy { ...@@ -1443,7 +1401,7 @@ func (x *Float) ucmp(y *Float) Accuracy {
// Rounding is performed according to z's precision and rounding mode; and // 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) // z's accuracy reports the result error relative to the exact (not rounded)
// result. // result.
// BUG(gri) Float.Add returns NaN if an operand is Inf. // BUG(gri) Float.Add panics if an operand is Inf.
// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect. // 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 {
...@@ -1459,7 +1417,7 @@ func (z *Float) Add(x, y *Float) *Float { ...@@ -1459,7 +1417,7 @@ func (z *Float) Add(x, y *Float) *Float {
if x.form != finite || y.form != finite { if x.form != finite || y.form != finite {
if x.form > finite || y.form > finite { if x.form > finite || y.form > finite {
// TODO(gri) handle Inf separately // TODO(gri) handle Inf separately
return z.SetNaN() panic("Inf operand")
} }
if x.form == zero { if x.form == zero {
z.Set(y) z.Set(y)
...@@ -1481,7 +1439,7 @@ func (z *Float) Add(x, y *Float) *Float { ...@@ -1481,7 +1439,7 @@ func (z *Float) Add(x, y *Float) *Float {
} else { } else {
// x + (-y) == x - y == -(y - x) // x + (-y) == x - y == -(y - x)
// (-x) + y == y - x == -(x - y) // (-x) + y == y - x == -(x - y)
if x.ucmp(y) == Above { if x.ucmp(y) > 0 {
z.usub(x, y) z.usub(x, y)
} else { } else {
z.neg = !z.neg z.neg = !z.neg
...@@ -1494,7 +1452,7 @@ func (z *Float) Add(x, y *Float) *Float { ...@@ -1494,7 +1452,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) Float.Sub returns NaN if an operand is Inf. // BUG(gri) Float.Sub panics 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 {
x.validate() x.validate()
...@@ -1509,7 +1467,7 @@ func (z *Float) Sub(x, y *Float) *Float { ...@@ -1509,7 +1467,7 @@ func (z *Float) Sub(x, y *Float) *Float {
if x.form != finite || y.form != finite { if x.form != finite || y.form != finite {
if x.form > finite || y.form > finite { if x.form > finite || y.form > finite {
// TODO(gri) handle Inf separately // TODO(gri) handle Inf separately
return z.SetNaN() panic("Inf operand")
} }
if x.form == zero { if x.form == zero {
z.Neg(y) z.Neg(y)
...@@ -1531,7 +1489,7 @@ func (z *Float) Sub(x, y *Float) *Float { ...@@ -1531,7 +1489,7 @@ func (z *Float) Sub(x, y *Float) *Float {
} else { } else {
// x - y == x - y == -(y - x) // x - y == x - y == -(y - x)
// (-x) - (-y) == y - x == -(x - y) // (-x) - (-y) == y - x == -(x - y)
if x.ucmp(y) == Above { if x.ucmp(y) > 0 {
z.usub(x, y) z.usub(x, y)
} else { } else {
z.neg = !z.neg z.neg = !z.neg
...@@ -1544,7 +1502,7 @@ func (z *Float) Sub(x, y *Float) *Float { ...@@ -1544,7 +1502,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) Float.Mul returns NaN if an operand is Inf. // BUG(gri) Float.Mul panics 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 {
x.validate() x.validate()
...@@ -1561,7 +1519,7 @@ func (z *Float) Mul(x, y *Float) *Float { ...@@ -1561,7 +1519,7 @@ func (z *Float) Mul(x, y *Float) *Float {
if x.form != finite || y.form != finite { if x.form != finite || y.form != finite {
if x.form > finite || y.form > finite { if x.form > finite || y.form > finite {
// TODO(gri) handle Inf separately // TODO(gri) handle Inf separately
return z.SetNaN() panic("Inf operand")
} }
// x == ±0 || y == ±0 // x == ±0 || y == ±0
z.acc = Exact z.acc = Exact
...@@ -1577,7 +1535,8 @@ func (z *Float) Mul(x, y *Float) *Float { ...@@ -1577,7 +1535,8 @@ 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) Float.Quo returns NaN if an operand is Inf. // Quo panics is both operands are 0.
// BUG(gri) Float.Quo panics 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 {
x.validate() x.validate()
...@@ -1595,12 +1554,12 @@ func (z *Float) Quo(x, y *Float) *Float { ...@@ -1595,12 +1554,12 @@ func (z *Float) Quo(x, y *Float) *Float {
if x.form != finite || y.form != finite { if x.form != finite || y.form != finite {
if x.form > finite || y.form > finite { if x.form > finite || y.form > finite {
// TODO(gri) handle Inf separately // TODO(gri) handle Inf separately
return z.SetNaN() panic("Inf operand")
} }
// x == ±0 || y == ±0 // x == ±0 || y == ±0
if x.form == zero { if x.form == zero {
if y.form == zero { if y.form == zero {
return z.SetNaN() panic("0/0")
} }
z.form = zero z.form = zero
return z return z
...@@ -1616,57 +1575,39 @@ func (z *Float) Quo(x, y *Float) *Float { ...@@ -1616,57 +1575,39 @@ func (z *Float) Quo(x, y *Float) *Float {
return z return z
} }
type cmpResult struct {
acc Accuracy
}
// Cmp compares x and y and returns: // Cmp compares x and y and returns:
// //
// Below if x < y // -1 if x < y
// Exact if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf) // 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf)
// Above if x > y // +1 if x > y
// Undef if any of x, y is NaN
// //
func (x *Float) Cmp(y *Float) cmpResult { func (x *Float) Cmp(y *Float) int {
if debugFloat { if debugFloat {
x.validate() x.validate()
y.validate() y.validate()
} }
if x.form == nan || y.form == nan {
return cmpResult{Undef}
}
mx := x.ord() mx := x.ord()
my := y.ord() my := y.ord()
switch { switch {
case mx < my: case mx < my:
return cmpResult{Below} return -1
case mx > my: case mx > my:
return cmpResult{Above} return +1
} }
// mx == my // mx == my
// only if |mx| == 1 we have to compare the mantissae // only if |mx| == 1 we have to compare the mantissae
switch mx { switch mx {
case -1: case -1:
return cmpResult{y.ucmp(x)} return y.ucmp(x)
case +1: case +1:
return cmpResult{x.ucmp(y)} return x.ucmp(y)
} }
return cmpResult{Exact} return 0
} }
// The following accessors simplify testing of Cmp results.
func (res cmpResult) Acc() Accuracy { return res.acc }
func (res cmpResult) Eql() bool { return res.acc == Exact }
func (res cmpResult) Neq() bool { return res.acc != Exact }
func (res cmpResult) Lss() bool { return res.acc == Below }
func (res cmpResult) Leq() bool { return res.acc&Above == 0 }
func (res cmpResult) Gtr() bool { return res.acc == Above }
func (res cmpResult) Geq() bool { return res.acc&Below == 0 }
// ord classifies x and returns: // ord classifies x and returns:
// //
// -2 if -Inf == x // -2 if -Inf == x
...@@ -1675,7 +1616,6 @@ func (res cmpResult) Geq() bool { return res.acc&Below == 0 } ...@@ -1675,7 +1616,6 @@ func (res cmpResult) Geq() bool { return res.acc&Below == 0 }
// +1 if 0 < x < +Inf // +1 if 0 < x < +Inf
// +2 if x == +Inf // +2 if x == +Inf
// //
// x must not be NaN.
func (x *Float) ord() int { func (x *Float) ord() int {
var m int var m int
switch x.form { switch x.form {
...@@ -1685,8 +1625,6 @@ func (x *Float) ord() int { ...@@ -1685,8 +1625,6 @@ func (x *Float) ord() int {
return 0 return 0
case inf: case inf:
m = 2 m = 2
default:
panic("unreachable")
} }
if x.neg { if x.neg {
m = -m m = -m
......
...@@ -69,7 +69,7 @@ func TestFloatZeroValue(t *testing.T) { ...@@ -69,7 +69,7 @@ func TestFloatZeroValue(t *testing.T) {
{1, 2, 0, 0, '*', (*Float).Mul}, {1, 2, 0, 0, '*', (*Float).Mul},
{2, 0, 1, 0, '*', (*Float).Mul}, {2, 0, 1, 0, '*', (*Float).Mul},
{0, 0, 0, 0, '/', (*Float).Quo}, // = Nan // {0, 0, 0, 0, '/', (*Float).Quo}, // panics
{0, 2, 1, 2, '/', (*Float).Quo}, {0, 2, 1, 2, '/', (*Float).Quo},
{1, 2, 0, 0, '/', (*Float).Quo}, // = +Inf {1, 2, 0, 0, '/', (*Float).Quo}, // = +Inf
{2, 0, 1, 0, '/', (*Float).Quo}, {2, 0, 1, 0, '/', (*Float).Quo},
...@@ -77,7 +77,7 @@ func TestFloatZeroValue(t *testing.T) { ...@@ -77,7 +77,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.IsFinite() { if !z.IsInf() {
got = int(z.int64()) got = int(z.int64())
} }
if got != test.want { if got != test.want {
...@@ -97,11 +97,9 @@ func makeFloat(s string) *Float { ...@@ -97,11 +97,9 @@ func makeFloat(s string) *Float {
case "-0": case "-0":
return x.Neg(&x) return x.Neg(&x)
case "Inf", "+Inf": case "Inf", "+Inf":
return x.SetInf(+1) return x.SetInf(false)
case "-Inf": case "-Inf":
return x.SetInf(-1) return x.SetInf(true)
case "NaN", "-NaN":
return x.SetNaN()
} }
x.SetPrec(1000) x.SetPrec(1000)
...@@ -123,7 +121,6 @@ func TestFloatSetPrec(t *testing.T) { ...@@ -123,7 +121,6 @@ func TestFloatSetPrec(t *testing.T) {
{"-0", 0, "-0", Exact}, {"-0", 0, "-0", Exact},
{"-Inf", 0, "-Inf", Exact}, {"-Inf", 0, "-Inf", Exact},
{"+Inf", 0, "+Inf", Exact}, {"+Inf", 0, "+Inf", Exact},
{"NaN", 0, "NaN", Exact},
{"123", 0, "0", Below}, {"123", 0, "0", Below},
{"-123", 0, "-0", Above}, {"-123", 0, "-0", Above},
...@@ -132,7 +129,6 @@ func TestFloatSetPrec(t *testing.T) { ...@@ -132,7 +129,6 @@ func TestFloatSetPrec(t *testing.T) {
{"-0", MaxPrec, "-0", Exact}, {"-0", MaxPrec, "-0", Exact},
{"-Inf", MaxPrec, "-Inf", Exact}, {"-Inf", MaxPrec, "-Inf", Exact},
{"+Inf", MaxPrec, "+Inf", Exact}, {"+Inf", MaxPrec, "+Inf", Exact},
{"NaN", MaxPrec, "NaN", Exact},
// just a few regular cases - general rounding is tested elsewhere // just a few regular cases - general rounding is tested elsewhere
{"1.5", 1, "2", Above}, {"1.5", 1, "2", Above},
...@@ -164,7 +160,6 @@ func TestFloatMinPrec(t *testing.T) { ...@@ -164,7 +160,6 @@ func TestFloatMinPrec(t *testing.T) {
{"-0", 0}, {"-0", 0},
{"+Inf", 0}, {"+Inf", 0},
{"-Inf", 0}, {"-Inf", 0},
{"NaN", 0},
{"1", 1}, {"1", 1},
{"2", 1}, {"2", 1},
{"3", 2}, {"3", 2},
...@@ -191,7 +186,6 @@ func TestFloatSign(t *testing.T) { ...@@ -191,7 +186,6 @@ func TestFloatSign(t *testing.T) {
{"+0", 0}, {"+0", 0},
{"+1", +1}, {"+1", +1},
{"+Inf", +1}, {"+Inf", +1},
{"NaN", 0},
} { } {
x := makeFloat(test.x) x := makeFloat(test.x)
s := x.Sign() s := x.Sign()
...@@ -201,13 +195,9 @@ func TestFloatSign(t *testing.T) { ...@@ -201,13 +195,9 @@ func TestFloatSign(t *testing.T) {
} }
} }
// feq(x, y) is like x.Cmp(y) == 0 but it also considers the sign of 0 (0 != -0). // alike(x, y) is like x.Cmp(y) == 0 but also considers the sign of 0 (0 != -0).
// Caution: Two NaN's are equal with this function! func alike(x, y *Float) bool {
func feq(x, y *Float) bool { return x.Cmp(y) == 0 && x.Signbit() == y.Signbit()
if x.IsNaN() || y.IsNaN() {
return x.IsNaN() && y.IsNaN()
}
return x.Cmp(y).Eql() && x.IsNeg() == y.IsNeg()
} }
func TestFloatMantExp(t *testing.T) { func TestFloatMantExp(t *testing.T) {
...@@ -222,7 +212,6 @@ func TestFloatMantExp(t *testing.T) { ...@@ -222,7 +212,6 @@ func TestFloatMantExp(t *testing.T) {
{"Inf", "+Inf", 0}, {"Inf", "+Inf", 0},
{"+Inf", "+Inf", 0}, {"+Inf", "+Inf", 0},
{"-Inf", "-Inf", 0}, {"-Inf", "-Inf", 0},
{"NaN", "NaN", 0},
{"1.5", "0.75", 1}, {"1.5", "0.75", 1},
{"1.024e3", "0.5", 11}, {"1.024e3", "0.5", 11},
{"-0.125", "-0.5", -2}, {"-0.125", "-0.5", -2},
...@@ -231,7 +220,7 @@ func TestFloatMantExp(t *testing.T) { ...@@ -231,7 +220,7 @@ func TestFloatMantExp(t *testing.T) {
mant := makeFloat(test.mant) mant := makeFloat(test.mant)
m := new(Float) m := new(Float)
e := x.MantExp(m) e := x.MantExp(m)
if !feq(m, mant) || e != test.exp { if !alike(m, mant) || e != test.exp {
t.Errorf("%s.MantExp() = %s, %d; want %s, %d", test.x, m.Format('g', 10), e, test.mant, test.exp) t.Errorf("%s.MantExp() = %s, %d; want %s, %d", test.x, m.Format('g', 10), e, test.mant, test.exp)
} }
} }
...@@ -242,7 +231,7 @@ func TestFloatMantExpAliasing(t *testing.T) { ...@@ -242,7 +231,7 @@ func TestFloatMantExpAliasing(t *testing.T) {
if e := x.MantExp(x); e != 10 { if e := x.MantExp(x); e != 10 {
t.Fatalf("Float.MantExp aliasing error: got %d; want 10", e) t.Fatalf("Float.MantExp aliasing error: got %d; want 10", e)
} }
if want := makeFloat("0.5"); !feq(x, want) { if want := makeFloat("0.5"); !alike(x, want) {
t.Fatalf("Float.MantExp aliasing error: got %s; want %s", x.Format('g', 10), want.Format('g', 10)) t.Fatalf("Float.MantExp aliasing error: got %s; want %s", x.Format('g', 10), want.Format('g', 10))
} }
} }
...@@ -274,12 +263,12 @@ func TestFloatSetMantExp(t *testing.T) { ...@@ -274,12 +263,12 @@ func TestFloatSetMantExp(t *testing.T) {
want := makeFloat(test.z) want := makeFloat(test.z)
var z Float var z Float
z.SetMantExp(frac, test.exp) z.SetMantExp(frac, test.exp)
if !feq(&z, want) { if !alike(&z, want) {
t.Errorf("SetMantExp(%s, %d) = %s; want %s", test.frac, test.exp, z.Format('g', 10), test.z) t.Errorf("SetMantExp(%s, %d) = %s; want %s", test.frac, test.exp, z.Format('g', 10), test.z)
} }
// test inverse property // test inverse property
mant := new(Float) mant := new(Float)
if z.SetMantExp(mant, want.MantExp(mant)).Cmp(want).Neq() { if z.SetMantExp(mant, want.MantExp(mant)).Cmp(want) != 0 {
t.Errorf("Inverse property not satisfied: got %s; want %s", z.Format('g', 10), test.z) t.Errorf("Inverse property not satisfied: got %s; want %s", z.Format('g', 10), test.z)
} }
} }
...@@ -288,32 +277,26 @@ func TestFloatSetMantExp(t *testing.T) { ...@@ -288,32 +277,26 @@ func TestFloatSetMantExp(t *testing.T) {
func TestFloatPredicates(t *testing.T) { func TestFloatPredicates(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
x string x string
neg, zero, finite, inf, nan bool sign int
signbit, inf bool
}{ }{
{x: "-Inf", neg: true, inf: true}, {x: "-Inf", sign: -1, signbit: true, inf: true},
{x: "-1", neg: true, finite: true}, {x: "-1", sign: -1, signbit: true},
{x: "-0", neg: true, zero: true, finite: true}, {x: "-0", signbit: true},
{x: "0", zero: true, finite: true}, {x: "0"},
{x: "1", finite: true}, {x: "1", sign: 1},
{x: "+Inf", inf: true}, {x: "+Inf", sign: 1, inf: true},
{x: "NaN", nan: true},
} { } {
x := makeFloat(test.x) x := makeFloat(test.x)
if got := x.IsNeg(); got != test.neg { if got := x.Signbit(); got != test.signbit {
t.Errorf("(%s).IsNeg() = %v; want %v", test.x, got, test.neg) t.Errorf("(%s).Signbit() = %v; want %v", test.x, got, test.signbit)
} }
if got := x.IsZero(); got != test.zero { if got := x.Sign(); got != test.sign {
t.Errorf("(%s).IsZero() = %v; want %v", test.x, got, test.zero) t.Errorf("(%s).Sign() = %d; want %d", test.x, got, test.sign)
}
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 { if got := x.IsInf(); got != test.inf {
t.Errorf("(%s).IsInf() = %v; want %v", test.x, 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)
}
} }
} }
...@@ -333,7 +316,6 @@ func TestFloatIsInt(t *testing.T) { ...@@ -333,7 +316,6 @@ func TestFloatIsInt(t *testing.T) {
"Inf", "Inf",
"+Inf", "+Inf",
"-Inf", "-Inf",
"NaN",
} { } {
s := strings.TrimSuffix(test, " int") s := strings.TrimSuffix(test, " int")
want := s != test want := s != test
...@@ -413,7 +395,7 @@ func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) { ...@@ -413,7 +395,7 @@ func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) {
// should be the same as rounding by SetInt64 after setting the // should be the same as rounding by SetInt64 after setting the
// precision) // precision)
g := new(Float).SetMode(mode).SetPrec(prec).SetInt64(x) g := new(Float).SetMode(mode).SetPrec(prec).SetInt64(x)
if !feq(g, f) { if !alike(g, f) {
t.Errorf("round %s (%d bits, %s) not symmetric: got %s and %s; want %s", t.Errorf("round %s (%d bits, %s) not symmetric: got %s and %s; want %s",
toBinary(x), prec, mode, toBinary(x), prec, mode,
toBinary(g.int64()), toBinary(g.int64()),
...@@ -426,7 +408,7 @@ func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) { ...@@ -426,7 +408,7 @@ func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) {
// h and f should be the same // h and f should be the same
// (repeated rounding should be idempotent) // (repeated rounding should be idempotent)
h := new(Float).SetMode(mode).SetPrec(prec).Set(f) h := new(Float).SetMode(mode).SetPrec(prec).Set(f)
if !feq(h, f) { if !alike(h, f) {
t.Errorf("round %s (%d bits, %s) not idempotent: got %s and %s; want %s", t.Errorf("round %s (%d bits, %s) not idempotent: got %s and %s; want %s",
toBinary(x), prec, mode, toBinary(x), prec, mode,
toBinary(h.int64()), toBinary(h.int64()),
...@@ -647,13 +629,6 @@ func TestFloatSetFloat64(t *testing.T) { ...@@ -647,13 +629,6 @@ func TestFloatSetFloat64(t *testing.T) {
} }
} }
// test NaN
var f Float
f.SetFloat64(math.NaN())
if got, acc := f.Float64(); !math.IsNaN(got) || acc != Undef {
t.Errorf("got %g (%s, %s); want %g (undef)", got, f.Format('p', 0), acc, math.NaN())
}
// test basic rounding behavior (exhaustive rounding testing is done elsewhere) // test basic rounding behavior (exhaustive rounding testing is done elsewhere)
const x uint64 = 0x8765432143218 // 53 bits needed const x uint64 = 0x8765432143218 // 53 bits needed
for prec := uint(1); prec <= 52; prec++ { for prec := uint(1); prec <= 52; prec++ {
...@@ -664,6 +639,17 @@ func TestFloatSetFloat64(t *testing.T) { ...@@ -664,6 +639,17 @@ func TestFloatSetFloat64(t *testing.T) {
t.Errorf("got %g (%s); want %g", got, f.Format('p', 0), want) t.Errorf("got %g (%s); want %g", got, f.Format('p', 0), want)
} }
} }
// test NaN
defer func() {
if p, ok := recover().(ErrNaN); !ok {
t.Errorf("got %v; want ErrNaN panic", p)
}
}()
var f Float
f.SetFloat64(math.NaN())
// should not reach here
t.Errorf("got %s; want ErrNaN panic", f.Format('p', 0))
} }
func TestFloatSetInt(t *testing.T) { func TestFloatSetInt(t *testing.T) {
...@@ -747,20 +733,18 @@ func TestFloatSetRat(t *testing.T) { ...@@ -747,20 +733,18 @@ func TestFloatSetRat(t *testing.T) {
func TestFloatSetInf(t *testing.T) { func TestFloatSetInf(t *testing.T) {
var f Float var f Float
for _, test := range []struct { for _, test := range []struct {
sign int signbit bool
prec uint prec uint
want string want string
}{ }{
{0, 0, "+Inf"}, {false, 0, "+Inf"},
{100, 0, "+Inf"}, {true, 0, "-Inf"},
{-1, 0, "-Inf"}, {false, 10, "+Inf"},
{0, 10, "+Inf"}, {true, 30, "-Inf"},
{100, 20, "+Inf"},
{-1, 30, "-Inf"},
} { } {
x := f.SetPrec(test.prec).SetInf(test.sign) x := f.SetPrec(test.prec).SetInf(test.signbit)
if got := x.String(); got != test.want || x.Prec() != test.prec { if got := x.String(); got != test.want || x.Prec() != test.prec {
t.Errorf("SetInf(%d) = %s (prec = %d); want %s (prec = %d)", test.sign, got, x.Prec(), test.want, test.prec) t.Errorf("SetInf(%v) = %s (prec = %d); want %s (prec = %d)", test.signbit, got, x.Prec(), test.want, test.prec)
} }
} }
} }
...@@ -786,7 +770,6 @@ func TestFloatUint64(t *testing.T) { ...@@ -786,7 +770,6 @@ func TestFloatUint64(t *testing.T) {
{"18446744073709551616", math.MaxUint64, Below}, {"18446744073709551616", math.MaxUint64, Below},
{"1e10000", math.MaxUint64, Below}, {"1e10000", math.MaxUint64, Below},
{"+Inf", math.MaxUint64, Below}, {"+Inf", math.MaxUint64, Below},
{"NaN", 0, Undef},
} { } {
x := makeFloat(test.x) x := makeFloat(test.x)
out, acc := x.Uint64() out, acc := x.Uint64()
...@@ -827,7 +810,6 @@ func TestFloatInt64(t *testing.T) { ...@@ -827,7 +810,6 @@ func TestFloatInt64(t *testing.T) {
{"9223372036854775808", math.MaxInt64, Below}, {"9223372036854775808", math.MaxInt64, Below},
{"1e10000", math.MaxInt64, Below}, {"1e10000", math.MaxInt64, Below},
{"+Inf", math.MaxInt64, Below}, {"+Inf", math.MaxInt64, Below},
{"NaN", 0, Undef},
} { } {
x := makeFloat(test.x) x := makeFloat(test.x)
out, acc := x.Int64() out, acc := x.Int64()
...@@ -891,12 +873,6 @@ func TestFloatFloat32(t *testing.T) { ...@@ -891,12 +873,6 @@ func TestFloatFloat32(t *testing.T) {
t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out) t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
} }
} }
// test NaN
x := makeFloat("NaN")
if out, acc := x.Float32(); out == out || acc != Undef {
t.Errorf("NaN: got %g (%s); want NaN (Undef)", out, acc)
}
} }
func TestFloatFloat64(t *testing.T) { func TestFloatFloat64(t *testing.T) {
...@@ -963,12 +939,6 @@ func TestFloatFloat64(t *testing.T) { ...@@ -963,12 +939,6 @@ func TestFloatFloat64(t *testing.T) {
t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out) t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
} }
} }
// test NaN
x := makeFloat("NaN")
if out, acc := x.Float64(); out == out || acc != Undef {
t.Errorf("NaN: got %g (%s); want NaN (Undef)", out, acc)
}
} }
func TestFloatInt(t *testing.T) { func TestFloatInt(t *testing.T) {
...@@ -983,7 +953,6 @@ func TestFloatInt(t *testing.T) { ...@@ -983,7 +953,6 @@ func TestFloatInt(t *testing.T) {
{"Inf", "nil", Below}, {"Inf", "nil", Below},
{"+Inf", "nil", Below}, {"+Inf", "nil", Below},
{"-Inf", "nil", Above}, {"-Inf", "nil", Above},
{"NaN", "nil", Undef},
{"1", "1", Exact}, {"1", "1", Exact},
{"-1", "-1", Exact}, {"-1", "-1", Exact},
{"1.23", "1", Below}, {"1.23", "1", Below},
...@@ -1028,7 +997,6 @@ func TestFloatRat(t *testing.T) { ...@@ -1028,7 +997,6 @@ func TestFloatRat(t *testing.T) {
{"Inf", "nil", Below}, {"Inf", "nil", Below},
{"+Inf", "nil", Below}, {"+Inf", "nil", Below},
{"-Inf", "nil", Above}, {"-Inf", "nil", Above},
{"NaN", "nil", Undef},
{"1", "1/1", Exact}, {"1", "1/1", Exact},
{"-1", "-1/1", Exact}, {"-1", "-1/1", Exact},
{"1.25", "5/4", Exact}, {"1.25", "5/4", Exact},
...@@ -1056,7 +1024,7 @@ func TestFloatRat(t *testing.T) { ...@@ -1056,7 +1024,7 @@ func TestFloatRat(t *testing.T) {
// inverse conversion // inverse conversion
if res != nil { if res != nil {
got := new(Float).SetPrec(64).SetRat(res) got := new(Float).SetPrec(64).SetRat(res)
if got.Cmp(x).Neq() { if got.Cmp(x) != 0 {
t.Errorf("%s: got %s; want %s", test.x, got, x) t.Errorf("%s: got %s; want %s", test.x, got, x)
} }
} }
...@@ -1081,17 +1049,16 @@ func TestFloatAbs(t *testing.T) { ...@@ -1081,17 +1049,16 @@ func TestFloatAbs(t *testing.T) {
"1e-1000", "1e-1000",
"1e1000", "1e1000",
"Inf", "Inf",
"NaN",
} { } {
p := makeFloat(test) p := makeFloat(test)
a := new(Float).Abs(p) a := new(Float).Abs(p)
if !feq(a, p) { if !alike(a, p) {
t.Errorf("%s: got %s; want %s", test, a.Format('g', 10), test) t.Errorf("%s: got %s; want %s", test, a.Format('g', 10), test)
} }
n := makeFloat("-" + test) n := makeFloat("-" + test)
a.Abs(n) a.Abs(n)
if !feq(a, p) { if !alike(a, p) {
t.Errorf("-%s: got %s; want %s", test, a.Format('g', 10), test) t.Errorf("-%s: got %s; want %s", test, a.Format('g', 10), test)
} }
} }
...@@ -1106,16 +1073,15 @@ func TestFloatNeg(t *testing.T) { ...@@ -1106,16 +1073,15 @@ func TestFloatNeg(t *testing.T) {
"1e-1000", "1e-1000",
"1e1000", "1e1000",
"Inf", "Inf",
"NaN",
} { } {
p1 := makeFloat(test) p1 := makeFloat(test)
n1 := makeFloat("-" + test) n1 := makeFloat("-" + test)
n2 := new(Float).Neg(p1) n2 := new(Float).Neg(p1)
p2 := new(Float).Neg(n2) p2 := new(Float).Neg(n2)
if !feq(n2, n1) { if !alike(n2, n1) {
t.Errorf("%s: got %s; want %s", test, n2.Format('g', 10), n1.Format('g', 10)) t.Errorf("%s: got %s; want %s", test, n2.Format('g', 10), n1.Format('g', 10))
} }
if !feq(p2, p1) { if !alike(p2, p1) {
t.Errorf("%s: got %s; want %s", test, p2.Format('g', 10), p1.Format('g', 10)) t.Errorf("%s: got %s; want %s", test, p2.Format('g', 10), p1.Format('g', 10))
} }
} }
...@@ -1133,7 +1099,7 @@ func TestFloatInc(t *testing.T) { ...@@ -1133,7 +1099,7 @@ func TestFloatInc(t *testing.T) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
x.Add(&x, &one) x.Add(&x, &one)
} }
if x.Cmp(new(Float).SetInt64(n)).Neq() { if x.Cmp(new(Float).SetInt64(n)) != 0 {
t.Errorf("prec = %d: got %s; want %d", prec, &x, n) t.Errorf("prec = %d: got %s; want %d", prec, &x, n)
} }
} }
...@@ -1174,14 +1140,14 @@ func TestFloatAdd(t *testing.T) { ...@@ -1174,14 +1140,14 @@ func TestFloatAdd(t *testing.T) {
got := new(Float).SetPrec(prec).SetMode(mode) got := new(Float).SetPrec(prec).SetMode(mode)
got.Add(x, y) got.Add(x, y)
want := zbits.round(prec, mode) want := zbits.round(prec, mode)
if got.Cmp(want).Neq() { if got.Cmp(want) != 0 {
t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t+ %s %v\n\t= %s\n\twant %s", t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t+ %s %v\n\t= %s\n\twant %s",
i, prec, mode, x, xbits, y, ybits, got, want) i, prec, mode, x, xbits, y, ybits, got, want)
} }
got.Sub(z, x) got.Sub(z, x)
want = ybits.round(prec, mode) want = ybits.round(prec, mode)
if got.Cmp(want).Neq() { if got.Cmp(want) != 0 {
t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t- %s %v\n\t= %s\n\twant %s", t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t- %s %v\n\t= %s\n\twant %s",
i, prec, mode, z, zbits, x, xbits, got, want) i, prec, mode, z, zbits, x, xbits, got, want)
} }
...@@ -1276,17 +1242,17 @@ func TestFloatMul(t *testing.T) { ...@@ -1276,17 +1242,17 @@ func TestFloatMul(t *testing.T) {
got := new(Float).SetPrec(prec).SetMode(mode) got := new(Float).SetPrec(prec).SetMode(mode)
got.Mul(x, y) got.Mul(x, y)
want := zbits.round(prec, mode) want := zbits.round(prec, mode)
if got.Cmp(want).Neq() { if got.Cmp(want) != 0 {
t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t* %s %v\n\t= %s\n\twant %s", t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t* %s %v\n\t= %s\n\twant %s",
i, prec, mode, x, xbits, y, ybits, got, want) i, prec, mode, x, xbits, y, ybits, got, want)
} }
if x.IsZero() { if x.Sign() == 0 {
continue // ignore div-0 case (not invertable) continue // ignore div-0 case (not invertable)
} }
got.Quo(z, x) got.Quo(z, x)
want = ybits.round(prec, mode) want = ybits.round(prec, mode)
if got.Cmp(want).Neq() { if got.Cmp(want) != 0 {
t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t/ %s %v\n\t= %s\n\twant %s", t.Errorf("i = %d, prec = %d, %s:\n\t %s %v\n\t/ %s %v\n\t= %s\n\twant %s",
i, prec, mode, z, zbits, x, xbits, got, want) i, prec, mode, z, zbits, x, xbits, got, want)
} }
...@@ -1369,7 +1335,7 @@ func TestIssue6866(t *testing.T) { ...@@ -1369,7 +1335,7 @@ func TestIssue6866(t *testing.T) {
p.Mul(p, psix) p.Mul(p, psix)
z2.Sub(two, p) z2.Sub(two, p)
if z1.Cmp(z2).Neq() { if z1.Cmp(z2) != 0 {
t.Fatalf("prec %d: got z1 = %s != z2 = %s; want z1 == z2\n", prec, z1, z2) t.Fatalf("prec %d: got z1 = %s != z2 = %s; want z1 == z2\n", prec, z1, z2)
} }
if z1.Sign() != 0 { if z1.Sign() != 0 {
...@@ -1420,7 +1386,7 @@ func TestFloatQuo(t *testing.T) { ...@@ -1420,7 +1386,7 @@ func TestFloatQuo(t *testing.T) {
prec := uint(preci + d) prec := uint(preci + d)
got := new(Float).SetPrec(prec).SetMode(mode).Quo(x, y) got := new(Float).SetPrec(prec).SetMode(mode).Quo(x, y)
want := bits.round(prec, mode) want := bits.round(prec, mode)
if got.Cmp(want).Neq() { if got.Cmp(want) != 0 {
t.Errorf("i = %d, prec = %d, %s:\n\t %s\n\t/ %s\n\t= %s\n\twant %s", t.Errorf("i = %d, prec = %d, %s:\n\t %s\n\t/ %s\n\t= %s\n\twant %s",
i, prec, mode, x, y, got, want) i, prec, mode, x, y, got, want)
} }
...@@ -1472,10 +1438,10 @@ func TestFloatQuoSmoke(t *testing.T) { ...@@ -1472,10 +1438,10 @@ func TestFloatQuoSmoke(t *testing.T) {
// TestFloatArithmeticSpecialValues tests that Float operations produce the // TestFloatArithmeticSpecialValues tests that Float operations produce the
// correct results for combinations of zero (±0), finite (±1 and ±2.71828), // correct results for combinations of zero (±0), finite (±1 and ±2.71828),
// and non-finite (±Inf, NaN) operands. // and non-finite (±Inf) operands.
func TestFloatArithmeticSpecialValues(t *testing.T) { func TestFloatArithmeticSpecialValues(t *testing.T) {
zero := 0.0 zero := 0.0
args := []float64{math.Inf(-1), -2.71828, -1, -zero, zero, 1, 2.71828, math.Inf(1), math.NaN()} args := []float64{math.Inf(-1), -2.71828, -1, -zero, zero, 1, 2.71828, math.Inf(1)}
xx := new(Float) xx := new(Float)
yy := new(Float) yy := new(Float)
got := new(Float) got := new(Float)
...@@ -1486,10 +1452,15 @@ func TestFloatArithmeticSpecialValues(t *testing.T) { ...@@ -1486,10 +1452,15 @@ func TestFloatArithmeticSpecialValues(t *testing.T) {
// check conversion is correct // check conversion is correct
// (no need to do this for y, since we see exactly the // (no need to do this for y, since we see exactly the
// same values there) // same values there)
if got, acc := xx.Float64(); !math.IsNaN(x) && (got != x || acc != Exact) { if got, acc := xx.Float64(); got != x || acc != Exact {
t.Errorf("Float(%g) == %g (%s)", x, got, acc) t.Errorf("Float(%g) == %g (%s)", x, got, acc)
} }
for _, y := range args { for _, y := range args {
// At the moment an Inf operand always leads to a panic (known bug).
// TODO(gri) remove this once the bug is fixed.
if math.IsInf(x, 0) || math.IsInf(y, 0) {
continue
}
yy.SetFloat64(y) yy.SetFloat64(y)
var op string var op string
var z float64 var z float64
...@@ -1507,20 +1478,18 @@ func TestFloatArithmeticSpecialValues(t *testing.T) { ...@@ -1507,20 +1478,18 @@ func TestFloatArithmeticSpecialValues(t *testing.T) {
z = x * y z = x * y
got.Mul(xx, yy) got.Mul(xx, yy)
case 3: case 3:
if x == 0 && y == 0 {
// TODO(gri) check for ErrNaN
continue // 0/0 panics with ErrNaN
}
op = "/" op = "/"
z = x / y z = x / y
got.Quo(xx, yy) got.Quo(xx, yy)
default: default:
panic("unreachable") panic("unreachable")
} }
// At the moment an Inf operand always leads to a NaN result (known bug).
// TODO(gri) remove this once the bug is fixed.
if math.IsInf(x, 0) || math.IsInf(y, 0) {
want.SetNaN()
} else {
want.SetFloat64(z) want.SetFloat64(z)
} if !alike(got, want) {
if !feq(got, want) {
t.Errorf("%5g %s %5g = %5s; want %5s", x, op, y, got, want) t.Errorf("%5g %s %5g = %5s; want %5s", x, op, y, got, want)
} }
} }
...@@ -1645,11 +1614,11 @@ func TestFloatArithmeticRounding(t *testing.T) { ...@@ -1645,11 +1614,11 @@ func TestFloatArithmeticRounding(t *testing.T) {
} }
// TestFloatCmpSpecialValues tests that Cmp produces the correct results for // TestFloatCmpSpecialValues tests that Cmp produces the correct results for
// combinations of zero (±0), finite (±1 and ±2.71828), and non-finite (±Inf, // combinations of zero (±0), finite (±1 and ±2.71828), and non-finite (±Inf)
// NaN) operands. // operands.
func TestFloatCmpSpecialValues(t *testing.T) { func TestFloatCmpSpecialValues(t *testing.T) {
zero := 0.0 zero := 0.0
args := []float64{math.Inf(-1), -2.71828, -1, -zero, zero, 1, 2.71828, math.Inf(1), math.NaN()} args := []float64{math.Inf(-1), -2.71828, -1, -zero, zero, 1, 2.71828, math.Inf(1)}
xx := new(Float) xx := new(Float)
yy := new(Float) yy := new(Float)
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
...@@ -1658,20 +1627,18 @@ func TestFloatCmpSpecialValues(t *testing.T) { ...@@ -1658,20 +1627,18 @@ func TestFloatCmpSpecialValues(t *testing.T) {
// check conversion is correct // check conversion is correct
// (no need to do this for y, since we see exactly the // (no need to do this for y, since we see exactly the
// same values there) // same values there)
if got, acc := xx.Float64(); !math.IsNaN(x) && (got != x || acc != Exact) { if got, acc := xx.Float64(); got != x || acc != Exact {
t.Errorf("Float(%g) == %g (%s)", x, got, acc) t.Errorf("Float(%g) == %g (%s)", x, got, acc)
} }
for _, y := range args { for _, y := range args {
yy.SetFloat64(y) yy.SetFloat64(y)
got := xx.Cmp(yy).Acc() got := xx.Cmp(yy)
want := Undef want := 0
switch { switch {
case x < y: case x < y:
want = Below want = -1
case x == y:
want = Exact
case x > y: case x > y:
want = Above want = +1
} }
if got != want { if got != want {
t.Errorf("(%g).Cmp(%g) = %s; want %s", x, y, got, want) t.Errorf("(%g).Cmp(%g) = %s; want %s", x, y, got, want)
......
...@@ -73,10 +73,8 @@ func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) { ...@@ -73,10 +73,8 @@ func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
prec = 64 prec = 64
} }
// NaNs ignore sign, mantissa, and exponent so we can set // A reasonable value in case of an error.
// them below while having a valid value for z in case of z.form = zero
// errors.
z.SetNaN()
// sign // sign
z.neg, err = scanSign(r) z.neg, err = scanSign(r)
...@@ -260,11 +258,6 @@ func (x *Float) Append(buf []byte, format byte, prec int) []byte { ...@@ -260,11 +258,6 @@ func (x *Float) Append(buf []byte, format byte, prec int) []byte {
return append(buf, "Inf"...) return append(buf, "Inf"...)
} }
// NaN
if x.IsNaN() {
return append(buf, "NaN"...)
}
// easy formats // easy formats
switch format { switch format {
case 'b': case 'b':
......
...@@ -102,7 +102,7 @@ func TestFloatSetFloat64String(t *testing.T) { ...@@ -102,7 +102,7 @@ func TestFloatSetFloat64String(t *testing.T) {
} }
f, _ := x.Float64() f, _ := x.Float64()
want := new(Float).SetFloat64(test.x) want := new(Float).SetFloat64(test.x)
if x.Cmp(want).Neq() { if x.Cmp(want) != 0 {
t.Errorf("%s: got %s (%v); want %v", test.s, &x, f, test.x) t.Errorf("%s: got %s (%v); want %v", test.s, &x, f, test.x)
} }
} }
......
...@@ -50,88 +50,62 @@ func Example_Shift() { ...@@ -50,88 +50,62 @@ func Example_Shift() {
func ExampleFloat_Cmp() { func ExampleFloat_Cmp() {
inf := math.Inf(1) inf := math.Inf(1)
zero := 0.0 zero := 0.0
nan := math.NaN()
operands := []float64{-inf, -1.2, -zero, 0, +1.2, +inf, nan} operands := []float64{-inf, -1.2, -zero, 0, +1.2, +inf}
fmt.Println(" x y cmp eql neq lss leq gtr geq") fmt.Println(" x y cmp")
fmt.Println("-----------------------------------------------") fmt.Println("---------------")
for _, x64 := range operands { for _, x64 := range operands {
x := big.NewFloat(x64) x := big.NewFloat(x64)
for _, y64 := range operands { for _, y64 := range operands {
y := big.NewFloat(y64) y := big.NewFloat(y64)
t := x.Cmp(y) fmt.Printf("%4s %4s %3d\n", x, y, x.Cmp(y))
fmt.Printf(
"%4s %4s %5s %c %c %c %c %c %c\n",
x, y, t.Acc(),
mark(t.Eql()), mark(t.Neq()), mark(t.Lss()), mark(t.Leq()), mark(t.Gtr()), mark(t.Geq()))
} }
fmt.Println() fmt.Println()
} }
// Output: // Output:
// x y cmp eql neq lss leq gtr geq // x y cmp
// ----------------------------------------------- // ---------------
// -Inf -Inf Exact ● ○ ○ ● ○ ● // -Inf -Inf 0
// -Inf -1.2 Below ○ ● ● ● ○ ○ // -Inf -1.2 -1
// -Inf -0 Below ○ ● ● ● ○ ○ // -Inf -0 -1
// -Inf 0 Below ○ ● ● ● ○ ○ // -Inf 0 -1
// -Inf 1.2 Below ○ ● ● ● ○ ○ // -Inf 1.2 -1
// -Inf +Inf Below ○ ● ● ● ○ ○ // -Inf +Inf -1
// -Inf NaN Undef ○ ● ○ ○ ○ ○
// //
// -1.2 -Inf Above ○ ● ○ ○ ● ● // -1.2 -Inf 1
// -1.2 -1.2 Exact ● ○ ○ ● ○ ● // -1.2 -1.2 0
// -1.2 -0 Below ○ ● ● ● ○ ○ // -1.2 -0 -1
// -1.2 0 Below ○ ● ● ● ○ ○ // -1.2 0 -1
// -1.2 1.2 Below ○ ● ● ● ○ ○ // -1.2 1.2 -1
// -1.2 +Inf Below ○ ● ● ● ○ ○ // -1.2 +Inf -1
// -1.2 NaN Undef ○ ● ○ ○ ○ ○
// //
// -0 -Inf Above ○ ● ○ ○ ● ● // -0 -Inf 1
// -0 -1.2 Above ○ ● ○ ○ ● ● // -0 -1.2 1
// -0 -0 Exact ● ○ ○ ● ○ ● // -0 -0 0
// -0 0 Exact ● ○ ○ ● ○ ● // -0 0 0
// -0 1.2 Below ○ ● ● ● ○ ○ // -0 1.2 -1
// -0 +Inf Below ○ ● ● ● ○ ○ // -0 +Inf -1
// -0 NaN Undef ○ ● ○ ○ ○ ○
// //
// 0 -Inf Above ○ ● ○ ○ ● ● // 0 -Inf 1
// 0 -1.2 Above ○ ● ○ ○ ● ● // 0 -1.2 1
// 0 -0 Exact ● ○ ○ ● ○ ● // 0 -0 0
// 0 0 Exact ● ○ ○ ● ○ ● // 0 0 0
// 0 1.2 Below ○ ● ● ● ○ ○ // 0 1.2 -1
// 0 +Inf Below ○ ● ● ● ○ ○ // 0 +Inf -1
// 0 NaN Undef ○ ● ○ ○ ○ ○
// //
// 1.2 -Inf Above ○ ● ○ ○ ● ● // 1.2 -Inf 1
// 1.2 -1.2 Above ○ ● ○ ○ ● ● // 1.2 -1.2 1
// 1.2 -0 Above ○ ● ○ ○ ● ● // 1.2 -0 1
// 1.2 0 Above ○ ● ○ ○ ● ● // 1.2 0 1
// 1.2 1.2 Exact ● ○ ○ ● ○ ● // 1.2 1.2 0
// 1.2 +Inf Below ○ ● ● ● ○ ○ // 1.2 +Inf -1
// 1.2 NaN Undef ○ ● ○ ○ ○ ○
// //
// +Inf -Inf Above ○ ● ○ ○ ● ● // +Inf -Inf 1
// +Inf -1.2 Above ○ ● ○ ○ ● ● // +Inf -1.2 1
// +Inf -0 Above ○ ● ○ ○ ● ● // +Inf -0 1
// +Inf 0 Above ○ ● ○ ○ ● ● // +Inf 0 1
// +Inf 1.2 Above ○ ● ○ ○ ● ● // +Inf 1.2 1
// +Inf +Inf Exact ● ○ ○ ● ○ ● // +Inf +Inf 0
// +Inf NaN Undef ○ ● ○ ○ ○ ○
//
// NaN -Inf Undef ○ ● ○ ○ ○ ○
// NaN -1.2 Undef ○ ● ○ ○ ○ ○
// NaN -0 Undef ○ ● ○ ○ ○ ○
// NaN 0 Undef ○ ● ○ ○ ○ ○
// NaN 1.2 Undef ○ ● ○ ○ ○ ○
// NaN +Inf Undef ○ ● ○ ○ ○ ○
// NaN NaN Undef ○ ● ○ ○ ○ ○
}
func mark(p bool) rune {
if p {
return '●'
}
return '○'
} }
...@@ -19,7 +19,7 @@ import "strconv" ...@@ -19,7 +19,7 @@ import "strconv"
// bigFtoa formats a float for the %e, %E, %f, %g, and %G formats. // bigFtoa formats a float for the %e, %E, %f, %g, and %G formats.
func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte { func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte {
if debugFloat && !f.IsFinite() { if debugFloat && f.IsInf() {
panic("non-finite float") panic("non-finite float")
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment