Commit d934d10a authored by Robert Griesemer's avatar Robert Griesemer

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/6650Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent ea1fafbc
...@@ -87,30 +87,31 @@ const ( ...@@ -87,30 +87,31 @@ 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 may be Undef (either Below or Above) for operations on
// -1: below exact value // and resulting in NaNs.
// 0: exact value
// +1: above exact value
//
type Accuracy int8 type Accuracy int8
// 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
Above Accuracy = +1 Below Accuracy = 1 << 0
Above Accuracy = 1 << 1
Undef Accuracy = Below | Above
) )
func (a Accuracy) String() string { func (a Accuracy) String() string {
switch { switch a {
case a < 0: case Exact:
return "below"
default:
return "exact" return "exact"
case a > 0: case Below:
return "below"
case Above:
return "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
...@@ -391,6 +392,9 @@ func (z *Float) round(sbit uint) { ...@@ -391,6 +392,9 @@ func (z *Float) round(sbit uint) {
// handle zero, Inf, and NaN // handle zero, Inf, and NaN
m := uint32(len(z.mant)) // present mantissa length in words m := uint32(len(z.mant)) // present mantissa length in words
if m == 0 { if m == 0 {
if z.exp == nanExp {
z.acc = Undef
}
return return
} }
// m > 0 implies z.prec > 0 (checked by validate) // m > 0 implies z.prec > 0 (checked by validate)
...@@ -507,8 +511,8 @@ func (z *Float) round(sbit uint) { ...@@ -507,8 +511,8 @@ func (z *Float) round(sbit uint) {
z.mant[0] &^= lsb - 1 z.mant[0] &^= lsb - 1
// update accuracy // update accuracy
if z.neg { if z.acc != Exact && z.neg {
z.acc = -z.acc z.acc ^= Below | Above
} }
if debugFloat { if debugFloat {
...@@ -751,9 +755,8 @@ func high64(x nat) uint64 { ...@@ -751,9 +755,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, and (math.MaxUint64, Below) // The result is (0, Above) for x < 0, (math.MaxUint64, Below)
// for x > math.MaxUint64. // for x > math.MaxUint64, and (0, Undef) for NaNs.
// BUG(gri) not implemented for NaN
func (x *Float) Uint64() (uint64, Accuracy) { func (x *Float) Uint64() (uint64, Accuracy) {
if debugFloat { if debugFloat {
validate(x) validate(x)
...@@ -770,7 +773,7 @@ func (x *Float) Uint64() (uint64, Accuracy) { ...@@ -770,7 +773,7 @@ func (x *Float) Uint64() (uint64, Accuracy) {
} }
return math.MaxUint64, Below // +Inf return math.MaxUint64, Below // +Inf
case nanExp: case nanExp:
panic("unimplemented") return 0, Undef // NaN
} }
panic("unreachable") panic("unreachable")
} }
...@@ -799,9 +802,8 @@ func (x *Float) Uint64() (uint64, Accuracy) { ...@@ -799,9 +802,8 @@ func (x *Float) Uint64() (uint64, Accuracy) {
// Int64 returns the integer resulting from truncating x towards zero. // Int64 returns the integer resulting from truncating x towards zero.
// 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, and // The result is (math.MinInt64, Above) for x < math.MinInt64,
// (math.MaxInt64, Below) for x > math.MaxInt64. // (math.MaxInt64, Below) for x > math.MaxInt64, and (0, Undef) for NaNs.
// BUG(gri) incorrect result for NaN
func (x *Float) Int64() (int64, Accuracy) { func (x *Float) Int64() (int64, Accuracy) {
if debugFloat { if debugFloat {
validate(x) validate(x)
...@@ -818,8 +820,7 @@ func (x *Float) Int64() (int64, Accuracy) { ...@@ -818,8 +820,7 @@ func (x *Float) Int64() (int64, Accuracy) {
} }
return math.MaxInt64, Below // +Inf return math.MaxInt64, Below // +Inf
case nanExp: case nanExp:
// TODO(gri) fix this return 0, Undef // NaN
return 0, Exact
} }
panic("unreachable") panic("unreachable")
} }
...@@ -860,7 +861,7 @@ func (x *Float) Int64() (int64, Accuracy) { ...@@ -860,7 +861,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) accuracy incorrect for NaN, doesn't handle exponent overflow // BUG(gri) 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)
...@@ -882,7 +883,7 @@ func (x *Float) Float64() (float64, Accuracy) { ...@@ -882,7 +883,7 @@ func (x *Float) Float64() (float64, Accuracy) {
} }
return math.Inf(sign), Exact return math.Inf(sign), Exact
case nanExp: case nanExp:
return math.NaN(), Exact return math.NaN(), Undef
} }
panic("unreachable") panic("unreachable")
} }
...@@ -906,7 +907,6 @@ func (x *Float) Float64() (float64, Accuracy) { ...@@ -906,7 +907,6 @@ func (x *Float) Float64() (float64, Accuracy) {
// 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
// the result in z instead of allocating a new Int. // 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) { func (x *Float) Int(z *Int) (*Int, Accuracy) {
if debugFloat { if debugFloat {
validate(x) validate(x)
...@@ -930,8 +930,7 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) { ...@@ -930,8 +930,7 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) {
} }
return nil, Below return nil, Below
case nanExp: case nanExp:
// TODO(gri) fix accuracy for NaN return nil, Undef
return nil, Exact
} }
panic("unreachable") panic("unreachable")
} }
...@@ -976,7 +975,6 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) { ...@@ -976,7 +975,6 @@ func (x *Float) Int(z *Int) (*Int, Accuracy) {
// The result is Exact is x is not an Inf or NaN. // The result is Exact is x is not an Inf or NaN.
// 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.
// BUG(gri) incorrect accuracy for Inf, NaN.
func (x *Float) Rat(z *Rat) (*Rat, Accuracy) { func (x *Float) Rat(z *Rat) (*Rat, Accuracy) {
if debugFloat { if debugFloat {
validate(x) validate(x)
...@@ -1000,8 +998,7 @@ func (x *Float) Rat(z *Rat) (*Rat, Accuracy) { ...@@ -1000,8 +998,7 @@ func (x *Float) Rat(z *Rat) (*Rat, Accuracy) {
} }
return nil, Below return nil, Below
case nanExp: case nanExp:
// TODO(gri) fix accuracy return nil, Undef
return nil, Exact
} }
panic("unreachable") panic("unreachable")
} }
...@@ -1499,6 +1496,7 @@ func (z *Float) Rsh(x *Float, s uint) *Float { ...@@ -1499,6 +1496,7 @@ func (z *Float) Rsh(x *Float, s uint) *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) comparing NaN's is not implemented
// (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)
......
...@@ -70,7 +70,7 @@ func TestFloatZeroValue(t *testing.T) { ...@@ -70,7 +70,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}, // = +Inf {0, 0, 0, 0, '/', (*Float).Quo}, // = Nan
{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},
...@@ -78,7 +78,7 @@ func TestFloatZeroValue(t *testing.T) { ...@@ -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) { if !z.IsInf(0) && !z.IsNaN() {
got = int(z.int64()) got = int(z.int64())
} }
if got != test.want { if got != test.want {
...@@ -625,8 +625,8 @@ func TestFloatSetFloat64(t *testing.T) { ...@@ -625,8 +625,8 @@ func TestFloatSetFloat64(t *testing.T) {
// test NaN // test NaN
var f Float var f Float
f.SetFloat64(math.NaN()) f.SetFloat64(math.NaN())
if got, acc := f.Float64(); !math.IsNaN(got) || acc != Exact { if got, acc := f.Float64(); !math.IsNaN(got) || acc != Undef {
t.Errorf("got %g (%s, %s); want %g (exact)", got, f.Format('p', 0), acc, math.NaN()) 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)
...@@ -765,7 +765,7 @@ func TestFloatUint64(t *testing.T) { ...@@ -765,7 +765,7 @@ 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, Exact}, TODO(gri) enable once implemented {"NaN", 0, Undef},
} { } {
x := makeFloat(test.x) x := makeFloat(test.x)
out, acc := x.Uint64() out, acc := x.Uint64()
...@@ -804,7 +804,7 @@ func TestFloatInt64(t *testing.T) { ...@@ -804,7 +804,7 @@ 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, Exact}, TODO(gri) enable once implemented {"NaN", 0, Undef},
} { } {
x := makeFloat(test.x) x := makeFloat(test.x)
out, acc := x.Int64() out, acc := x.Int64()
...@@ -826,6 +826,7 @@ func TestFloatInt(t *testing.T) { ...@@ -826,6 +826,7 @@ 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},
...@@ -862,21 +863,23 @@ func TestFloatInt(t *testing.T) { ...@@ -862,21 +863,23 @@ func TestFloatInt(t *testing.T) {
func TestFloatRat(t *testing.T) { func TestFloatRat(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
x, want string x, want string
acc Accuracy
}{ }{
{"0", "0/1"}, {"0", "0/1", Exact},
{"+0", "0/1"}, {"+0", "0/1", Exact},
{"-0", "0/1"}, {"-0", "0/1", Exact},
{"Inf", "nil"}, {"Inf", "nil", Below},
{"+Inf", "nil"}, {"+Inf", "nil", Below},
{"-Inf", "nil"}, {"-Inf", "nil", Above},
{"1", "1/1"}, {"NaN", "nil", Undef},
{"-1", "-1/1"}, {"1", "1/1", Exact},
{"1.25", "5/4"}, {"-1", "-1/1", Exact},
{"-1.25", "-5/4"}, {"1.25", "5/4", Exact},
{"1e10", "10000000000/1"}, {"-1.25", "-5/4", Exact},
{"1p10", "1024/1"}, {"1e10", "10000000000/1", Exact},
{"-1p-10", "-1/1024"}, {"1p10", "1024/1", Exact},
{"3.14159265", "7244019449799623199/2305843009213693952"}, {"-1p-10", "-1/1024", Exact},
{"3.14159265", "7244019449799623199/2305843009213693952", Exact},
} { } {
x := makeFloat(test.x).SetPrec(64) x := makeFloat(test.x).SetPrec(64)
res, acc := x.Rat(nil) res, acc := x.Rat(nil)
...@@ -888,8 +891,10 @@ func TestFloatRat(t *testing.T) { ...@@ -888,8 +891,10 @@ func TestFloatRat(t *testing.T) {
t.Errorf("%s: got %s; want %s", test.x, got, test.want) t.Errorf("%s: got %s; want %s", test.x, got, test.want)
continue continue
} }
// TODO(gri) check accuracy if acc != test.acc {
_ = acc t.Errorf("%s: got %s; want %s", test.x, acc, test.acc)
continue
}
// inverse conversion // inverse conversion
if res != nil { if res != nil {
......
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