]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: completed Float.Uint64
authorRobert Griesemer <gri@golang.org>
Wed, 11 Feb 2015 00:38:51 +0000 (16:38 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 11 Feb 2015 17:03:08 +0000 (17:03 +0000)
Change-Id: Ib3738492a2ec8fc99323e687168b17b7239db6ad
Reviewed-on: https://go-review.googlesource.com/4511
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/float.go
src/math/big/float_test.go

index a8a1eead6ae8630687b4699611184313c0718038..3464192aee22ae987487deef59a050167e6ac2a5 100644 (file)
@@ -660,23 +660,66 @@ func high64(x nat) uint64 {
        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())
+       m := high64(x.mant)
+       s := x.exp
+       var i int64
+       if s >= 0 {
+               i = int64(m >> (64 - uint(s)))
+       }
        if x.neg {
-               return -v
+               return -i
        }
-       return v
+       return i
 }
 
 // Float64 returns the closest float64 value of x
index 58fab4605aafb5123816f57fc66638735de3e628..3ec8e831315d2a56323e3fc979aaf3871ed80f17 100644 (file)
@@ -13,6 +13,14 @@ import (
        "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 @@ func TestFloatZeroValue(t *testing.T) {
                {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 @@ func TestFloatSetUint64(t *testing.T) {
        } {
                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 @@ func TestFloatSetUint64(t *testing.T) {
        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 TestFloatSetRat(t *testing.T) {
        }
 }
 
+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