math/big: completed Float.Uint64
Change-Id: Ib3738492a2ec8fc99323e687168b17b7239db6ad
Reviewed-on: https://go-review.googlesource.com/4511
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/src/math/big/float.go b/src/math/big/float.go
index a8a1eea..3464192 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -660,23 +660,66 @@
return v
}
-// TODO(gri) FIX THIS (Inf, rounding mode, errors, accuracy, etc.)
-func (x *Float) Uint64() uint64 {
- m := high64(x.mant)
- s := x.exp
- if s >= 0 {
- return m >> (64 - uint(s))
+// Uint64 returns the unsigned integer resulting from truncating x
+// towards zero. If 0 <= x < 2**64, the result is Exact if x is an
+// integer; and Below if x has a fractional part. The result is (0,
+// Above) for x < 0, and (math.MaxUint64, Below) for x > math.MaxUint64.
+func (x *Float) Uint64() (uint64, Accuracy) {
+ // TODO(gri) there ought to be an easier way to implement this efficiently
+ if debugFloat {
+ x.validate()
}
- return 0 // imprecise
+ // pick off easy cases
+ if x.exp <= 0 {
+ // |x| < 1 || |x| == Inf
+ if x.exp == infExp {
+ // ±Inf
+ if x.neg {
+ return 0, Above // -Inf
+ }
+ return math.MaxUint64, Below // +Inf
+ }
+ if len(x.mant) == 0 {
+ return 0, Exact // ±0
+ }
+ // 0 < |x| < 1
+ if x.neg {
+ return 0, Above
+ }
+ return 0, Below
+ }
+ // x.exp > 0
+ if x.neg {
+ return 0, Above
+ }
+ // x > 0
+ if x.exp <= 64 {
+ // u = trunc(x) fits into a uint64
+ u := high64(x.mant) >> (64 - uint32(x.exp))
+ // x.mant[len(x.mant)-1] != 0
+ // determine minimum required precision for x
+ minPrec := uint(len(x.mant))*_W - x.mant.trailingZeroBits()
+ if minPrec <= 64 {
+ return u, Exact
+ }
+ return u, Below
+ }
+ // x is too large
+ return math.MaxUint64, Below
}
// TODO(gri) FIX THIS (inf, rounding mode, errors, etc.)
func (x *Float) Int64() int64 {
- v := int64(x.Uint64())
- if x.neg {
- return -v
+ m := high64(x.mant)
+ s := x.exp
+ var i int64
+ if s >= 0 {
+ i = int64(m >> (64 - uint(s)))
}
- return v
+ if x.neg {
+ return -i
+ }
+ return i
}
// Float64 returns the closest float64 value of x
diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go
index 58fab46..3ec8e83 100644
--- a/src/math/big/float_test.go
+++ b/src/math/big/float_test.go
@@ -13,6 +13,14 @@
"testing"
)
+func (x *Float) uint64() uint64 {
+ u, acc := x.Uint64()
+ if acc != Exact {
+ panic(fmt.Sprintf("%s is not a uint64", x.Format('g', 10)))
+ }
+ return u
+}
+
func TestFloatZeroValue(t *testing.T) {
// zero (uninitialized) value is a ready-to-use 0.0
var x Float
@@ -52,14 +60,18 @@
{1, 2, 0, 0, '*', (*Float).Mul},
{2, 0, 1, 0, '*', (*Float).Mul},
- {0, 0, 0, 0, '/', (*Float).Quo},
+ {0, 0, 0, 0, '/', (*Float).Quo}, // = +Inf
{0, 2, 1, 2, '/', (*Float).Quo},
- {1, 2, 0, 0, '/', (*Float).Quo},
+ {1, 2, 0, 0, '/', (*Float).Quo}, // = +Inf
{2, 0, 1, 0, '/', (*Float).Quo},
} {
z := make(test.z)
test.op(z, make(test.x), make(test.y))
- if got := int(z.Int64()); got != test.want {
+ got := 0
+ if !z.IsInf(0) {
+ got = int(z.Int64())
+ }
+ if got != test.want {
t.Errorf("%d %c %d = %d; want %d", test.x, test.opname, test.y, got, test.want)
}
}
@@ -384,7 +396,7 @@
} {
var f Float
f.SetUint64(want)
- if got := f.Uint64(); got != want {
+ if got := f.uint64(); got != want {
t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want)
}
}
@@ -393,7 +405,7 @@
const x uint64 = 0x8765432187654321 // 64 bits needed
for prec := uint(1); prec <= 64; prec++ {
f := NewFloat(0, prec, ToZero).SetUint64(x)
- got := f.Uint64()
+ got := f.uint64()
want := x &^ (1<<(64-prec) - 1) // cut off (round to zero) low 64-prec bits
if got != want {
t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want)
@@ -553,6 +565,32 @@
}
}
+func TestFloatUint64(t *testing.T) {
+ for _, test := range []struct {
+ x string
+ out uint64
+ acc Accuracy
+ }{
+ {"0", 0, Exact},
+ {"-0", 0, Exact},
+ {"-1", 0, Above},
+ {"-Inf", 0, Above},
+ {"-1e-1000", 0, Above},
+ {"1e-1000", 0, Below},
+ {"12345.0", 12345, Exact},
+ {"12345.6", 12345, Below},
+ {"18446744073709551615", 18446744073709551615, Exact},
+ {"18446744073709551615.000000000000000000001", math.MaxUint64, Below},
+ {"1e10000", math.MaxUint64, Below},
+ } {
+ x := makeFloat(test.x)
+ out, acc := x.Uint64()
+ if out != test.out || acc != test.acc {
+ t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc)
+ }
+ }
+}
+
func TestFloatInt(t *testing.T) {
for _, test := range []struct {
x string