math/big: introduce Undef Accuracy, use for NaN operands/results
This change represents Accuracy as a bit pattern rather than
an ordered value; with a new value Undef which is both Below
and Above.
Change-Id: Ibb96294c1417fb3cf2c3cf2374c993b0a4e106b3
Reviewed-on: https://go-review.googlesource.com/6650
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/math/big/float.go b/src/math/big/float.go
index e133581..fa0cb2b 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -87,30 +87,31 @@
)
// Accuracy describes the rounding error produced by the most recent
-// operation that generated a Float value, relative to the exact value:
-//
-// -1: below exact value
-// 0: exact value
-// +1: above exact value
-//
+// operation that generated a Float value, relative to the exact value.
+// The accuracy may be Undef (either Below or Above) for operations on
+// and resulting in NaNs.
type Accuracy int8
// Constants describing the Accuracy of a Float.
const (
- Below Accuracy = -1
Exact Accuracy = 0
- Above Accuracy = +1
+ Below Accuracy = 1 << 0
+ Above Accuracy = 1 << 1
+ Undef Accuracy = Below | Above
)
func (a Accuracy) String() string {
- switch {
- case a < 0:
- return "below"
- default:
+ switch a {
+ case Exact:
return "exact"
- case a > 0:
+ 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
@@ -391,6 +392,9 @@
// handle zero, Inf, and NaN
m := uint32(len(z.mant)) // present mantissa length in words
if m == 0 {
+ if z.exp == nanExp {
+ z.acc = Undef
+ }
return
}
// m > 0 implies z.prec > 0 (checked by validate)
@@ -507,8 +511,8 @@
z.mant[0] &^= lsb - 1
// update accuracy
- if z.neg {
- z.acc = -z.acc
+ if z.acc != Exact && z.neg {
+ z.acc ^= Below | Above
}
if debugFloat {
@@ -751,9 +755,8 @@
// Uint64 returns the unsigned integer resulting from truncating x
// towards zero. If 0 <= x <= math.MaxUint64, the result is Exact
// if x is an integer and Below otherwise.
-// The result is (0, Above) for x < 0, and (math.MaxUint64, Below)
-// for x > math.MaxUint64.
-// BUG(gri) not implemented for NaN
+// The result is (0, Above) for x < 0, (math.MaxUint64, Below)
+// for x > math.MaxUint64, and (0, Undef) for NaNs.
func (x *Float) Uint64() (uint64, Accuracy) {
if debugFloat {
validate(x)
@@ -770,7 +773,7 @@
}
return math.MaxUint64, Below // +Inf
case nanExp:
- panic("unimplemented")
+ return 0, Undef // NaN
}
panic("unreachable")
}
@@ -799,9 +802,8 @@
// Int64 returns the integer resulting from truncating x towards zero.
// If math.MinInt64 <= x <= math.MaxInt64, the result is Exact if x is
// an integer, and Above (x < 0) or Below (x > 0) otherwise.
-// The result is (math.MinInt64, Above) for x < math.MinInt64, and
-// (math.MaxInt64, Below) for x > math.MaxInt64.
-// BUG(gri) incorrect result for NaN
+// The result is (math.MinInt64, Above) for x < math.MinInt64,
+// (math.MaxInt64, Below) for x > math.MaxInt64, and (0, Undef) for NaNs.
func (x *Float) Int64() (int64, Accuracy) {
if debugFloat {
validate(x)
@@ -818,8 +820,7 @@
}
return math.MaxInt64, Below // +Inf
case nanExp:
- // TODO(gri) fix this
- return 0, Exact
+ return 0, Undef // NaN
}
panic("unreachable")
}
@@ -860,7 +861,7 @@
// Float64 returns the closest float64 value of x
// by rounding to nearest with 53 bits precision.
-// BUG(gri) accuracy incorrect for NaN, doesn't handle exponent overflow
+// BUG(gri) doesn't handle exponent overflow
func (x *Float) Float64() (float64, Accuracy) {
if debugFloat {
validate(x)
@@ -882,7 +883,7 @@
}
return math.Inf(sign), Exact
case nanExp:
- return math.NaN(), Exact
+ return math.NaN(), Undef
}
panic("unreachable")
}
@@ -906,7 +907,6 @@
// for x > 0, and Above for x < 0.
// If a non-nil *Int argument z is provided, Int stores
// the result in z instead of allocating a new Int.
-// BUG(gri) accuracy incorrect for for NaN
func (x *Float) Int(z *Int) (*Int, Accuracy) {
if debugFloat {
validate(x)
@@ -930,8 +930,7 @@
}
return nil, Below
case nanExp:
- // TODO(gri) fix accuracy for NaN
- return nil, Exact
+ return nil, Undef
}
panic("unreachable")
}
@@ -976,7 +975,6 @@
// The result is Exact is x is not an Inf or NaN.
// If a non-nil *Rat argument z is provided, Rat stores
// the result in z instead of allocating a new Rat.
-// BUG(gri) incorrect accuracy for Inf, NaN.
func (x *Float) Rat(z *Rat) (*Rat, Accuracy) {
if debugFloat {
validate(x)
@@ -1000,8 +998,7 @@
}
return nil, Below
case nanExp:
- // TODO(gri) fix accuracy
- return nil, Exact
+ return nil, Undef
}
panic("unreachable")
}
@@ -1499,6 +1496,7 @@
// 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?)
func (x *Float) Cmp(y *Float) int {
if debugFloat {
validate(x)
diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go
index aaf4970..aa1cb58 100644
--- a/src/math/big/float_test.go
+++ b/src/math/big/float_test.go
@@ -70,7 +70,7 @@
{1, 2, 0, 0, '*', (*Float).Mul},
{2, 0, 1, 0, '*', (*Float).Mul},
- {0, 0, 0, 0, '/', (*Float).Quo}, // = +Inf
+ {0, 0, 0, 0, '/', (*Float).Quo}, // = Nan
{0, 2, 1, 2, '/', (*Float).Quo},
{1, 2, 0, 0, '/', (*Float).Quo}, // = +Inf
{2, 0, 1, 0, '/', (*Float).Quo},
@@ -78,7 +78,7 @@
z := make(test.z)
test.op(z, make(test.x), make(test.y))
got := 0
- if !z.IsInf(0) {
+ if !z.IsInf(0) && !z.IsNaN() {
got = int(z.int64())
}
if got != test.want {
@@ -625,8 +625,8 @@
// test NaN
var f Float
f.SetFloat64(math.NaN())
- if got, acc := f.Float64(); !math.IsNaN(got) || acc != Exact {
- t.Errorf("got %g (%s, %s); want %g (exact)", got, f.Format('p', 0), acc, 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)
@@ -765,7 +765,7 @@
{"18446744073709551616", math.MaxUint64, Below},
{"1e10000", math.MaxUint64, Below},
{"+Inf", math.MaxUint64, Below},
- // {"NaN", 0, Exact}, TODO(gri) enable once implemented
+ {"NaN", 0, Undef},
} {
x := makeFloat(test.x)
out, acc := x.Uint64()
@@ -804,7 +804,7 @@
{"9223372036854775808", math.MaxInt64, Below},
{"1e10000", math.MaxInt64, Below},
{"+Inf", math.MaxInt64, Below},
- // {"NaN", 0, Exact}, TODO(gri) enable once implemented
+ {"NaN", 0, Undef},
} {
x := makeFloat(test.x)
out, acc := x.Int64()
@@ -826,6 +826,7 @@
{"Inf", "nil", Below},
{"+Inf", "nil", Below},
{"-Inf", "nil", Above},
+ {"NaN", "nil", Undef},
{"1", "1", Exact},
{"-1", "-1", Exact},
{"1.23", "1", Below},
@@ -862,21 +863,23 @@
func TestFloatRat(t *testing.T) {
for _, test := range []struct {
x, want string
+ acc Accuracy
}{
- {"0", "0/1"},
- {"+0", "0/1"},
- {"-0", "0/1"},
- {"Inf", "nil"},
- {"+Inf", "nil"},
- {"-Inf", "nil"},
- {"1", "1/1"},
- {"-1", "-1/1"},
- {"1.25", "5/4"},
- {"-1.25", "-5/4"},
- {"1e10", "10000000000/1"},
- {"1p10", "1024/1"},
- {"-1p-10", "-1/1024"},
- {"3.14159265", "7244019449799623199/2305843009213693952"},
+ {"0", "0/1", Exact},
+ {"+0", "0/1", Exact},
+ {"-0", "0/1", Exact},
+ {"Inf", "nil", Below},
+ {"+Inf", "nil", Below},
+ {"-Inf", "nil", Above},
+ {"NaN", "nil", Undef},
+ {"1", "1/1", Exact},
+ {"-1", "-1/1", Exact},
+ {"1.25", "5/4", Exact},
+ {"-1.25", "-5/4", Exact},
+ {"1e10", "10000000000/1", Exact},
+ {"1p10", "1024/1", Exact},
+ {"-1p-10", "-1/1024", Exact},
+ {"3.14159265", "7244019449799623199/2305843009213693952", Exact},
} {
x := makeFloat(test.x).SetPrec(64)
res, acc := x.Rat(nil)
@@ -888,8 +891,10 @@
t.Errorf("%s: got %s; want %s", test.x, got, test.want)
continue
}
- // TODO(gri) check accuracy
- _ = acc
+ if acc != test.acc {
+ t.Errorf("%s: got %s; want %s", test.x, acc, test.acc)
+ continue
+ }
// inverse conversion
if res != nil {