]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: implemented Float.Int64, simplified Float.Uint64
authorRobert Griesemer <gri@golang.org>
Wed, 11 Feb 2015 19:22:45 +0000 (11:22 -0800)
committerRobert Griesemer <gri@golang.org>
Thu, 12 Feb 2015 20:59:02 +0000 (20:59 +0000)
Change-Id: Ic270ffa7ec6f6dd4b0a951c64ad965447cce1417
Reviewed-on: https://go-review.googlesource.com/4571
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/float.go
src/math/big/float_test.go

index 3464192aee22ae987487deef59a050167e6ac2a5..06b3ae2f401b7e6205d71e539de3761e5b311d91 100644 (file)
@@ -240,13 +240,7 @@ func (x *Float) IsInt() bool {
                return len(x.mant) == 0 && x.exp != infExp
        }
        // x.exp > 0
-       if uint(x.exp) >= x.prec {
-               return true // not enough precision for fractional mantissa
-       }
-       // x.mant[len(x.mant)-1] != 0
-       // determine minimum required precision for x
-       minPrec := uint(len(x.mant))*_W - x.mant.trailingZeroBits()
-       return uint(x.exp) >= minPrec
+       return x.prec <= uint(x.exp) || x.minPrec() <= uint(x.exp) // not enough bits for fractional mantissa
 }
 
 // IsInf reports whether x is an infinity, according to sign.
@@ -660,66 +654,104 @@ func high64(x nat) uint64 {
        return v
 }
 
+// minPrec returns the minimum precision required to represent
+// x without loss of accuracy.
+// TODO(gri) this might be useful to export, perhaps under a better name
+func (x *Float) minPrec() uint {
+       return uint(len(x.mant))*_W - x.mant.trailingZeroBits()
+}
+
 // 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.
+// 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.
 func (x *Float) Uint64() (uint64, Accuracy) {
-       // TODO(gri) there ought to be an easier way to implement this efficiently
        if debugFloat {
                x.validate()
        }
-       // 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 {
+       switch x.ord() {
+       case -2, -1:
+               // x < 0
                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
+       case 0:
+               // x == 0 || x == -0
+               return 0, Exact
+       case 1:
+               // 0 < x < +Inf
+               if x.exp <= 0 {
+                       // 0 < x < 1
+                       return 0, Below
                }
-               return u, Below
+               // 1 <= x < +Inf
+               if x.exp <= 64 {
+                       // u = trunc(x) fits into a uint64
+                       u := high64(x.mant) >> (64 - uint32(x.exp))
+                       if x.minPrec() <= 64 {
+                               return u, Exact
+                       }
+                       return u, Below // x truncated
+               }
+               fallthrough // x too large
+       case 2:
+               // x == +Inf
+               return math.MaxUint64, Below
        }
-       // x is too large
-       return math.MaxUint64, Below
+       panic("unreachable")
 }
 
-// TODO(gri) FIX THIS (inf, rounding mode, errors, etc.)
-func (x *Float) Int64() int64 {
-       m := high64(x.mant)
-       s := x.exp
-       var i int64
-       if s >= 0 {
-               i = int64(m >> (64 - uint(s)))
+// 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.
+func (x *Float) Int64() (int64, Accuracy) {
+       if debugFloat {
+               x.validate()
        }
-       if x.neg {
-               return -i
+
+       switch x.ord() {
+       case -2:
+               // x == -Inf
+               return math.MinInt64, Above
+       case 0:
+               // x == 0 || x == -0
+               return 0, Exact
+       case -1, 1:
+               // 0 < |x| < +Inf
+               acc := Below
+               if x.neg {
+                       acc = Above
+               }
+               if x.exp <= 0 {
+                       // 0 < |x| < 1
+                       return 0, acc
+               }
+               // 1 <= |x| < +Inf
+               if x.exp <= 63 {
+                       // i = trunc(x) fits into an int64 (excluding math.MinInt64)
+                       i := int64(high64(x.mant) >> (64 - uint32(x.exp)))
+                       if x.neg {
+                               i = -i
+                       }
+                       if x.minPrec() <= 63 {
+                               return i, Exact
+                       }
+                       return i, acc // x truncated
+               }
+               if x.neg {
+                       // check for special case x == math.MinInt64 (i.e., x == -(0.5 << 64))
+                       if x.exp == 64 && x.minPrec() == 1 {
+                               acc = Exact
+                       }
+                       return math.MinInt64, acc
+               }
+               fallthrough
+       case 2:
+               // x == +Inf
+               return math.MaxInt64, Below
        }
-       return i
+       panic("unreachable")
 }
 
 // Float64 returns the closest float64 value of x
@@ -776,9 +808,8 @@ func (x *Float) Int() (res *Int, acc Accuracy) {
        // x.mant[len(x.mant)-1] != 0
        // determine minimum required precision for x
        allBits := uint(len(x.mant)) * _W
-       minPrec := allBits - x.mant.trailingZeroBits()
        exp := uint(x.exp)
-       if exp >= minPrec {
+       if x.minPrec() <= exp {
                acc = Exact
        }
        // shift mantissa as needed
@@ -1199,8 +1230,8 @@ func (x *Float) Cmp(y *Float) int {
                y.validate()
        }
 
-       mx := x.mag()
-       my := y.mag()
+       mx := x.ord()
+       my := y.ord()
        switch {
        case mx < my:
                return -1
@@ -1227,17 +1258,17 @@ func umax(x, y uint) uint {
        return y
 }
 
-// mag returns:
+// ord classifies x and returns:
 //
-//     -2 if x == -Inf
-//     -1 if x < 0
-//      0 if x == -0 or x == +0
-//     +1 if x > 0
+//     -2 if -Inf == x
+//     -1 if -Inf < x < 0
+//      0 if x == 0 (signed or unsigned)
+//     +1 if 0 < x < +Inf
 //     +2 if x == +Inf
 //
-// mag is a helper function for Cmp.
-func (x *Float) mag() int {
-       m := 1
+// TODO(gri) export (and remove IsInf)?
+func (x *Float) ord() int {
+       m := 1 // common case
        if len(x.mant) == 0 {
                m = 0
                if x.exp == infExp {
index 3ec8e831315d2a56323e3fc979aaf3871ed80f17..dd059ba4a5bc9bca4abb72ee99a1707ff8addd03 100644 (file)
@@ -21,6 +21,14 @@ func (x *Float) uint64() uint64 {
        return u
 }
 
+func (x *Float) int64() int64 {
+       i, acc := x.Int64()
+       if acc != Exact {
+               panic(fmt.Sprintf("%s is not an int64", x.Format('g', 10)))
+       }
+       return i
+}
+
 func TestFloatZeroValue(t *testing.T) {
        // zero (uninitialized) value is a ready-to-use 0.0
        var x Float
@@ -69,7 +77,7 @@ func TestFloatZeroValue(t *testing.T) {
                test.op(z, make(test.x), make(test.y))
                got := 0
                if !z.IsInf(0) {
-                       got = int(z.Int64())
+                       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)
@@ -257,7 +265,7 @@ func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) {
        f.Round(f, prec, mode)
 
        // check result
-       r1 := f.Int64()
+       r1 := f.int64()
        p1 := f.Precision()
        a1 := f.Accuracy()
        if r1 != r || p1 != prec || a1 != a {
@@ -430,7 +438,7 @@ func TestFloatSetInt64(t *testing.T) {
                        }
                        var f Float
                        f.SetInt64(want)
-                       if got := f.Int64(); got != want {
+                       if got := f.int64(); got != want {
                                t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want)
                        }
                }
@@ -440,7 +448,7 @@ func TestFloatSetInt64(t *testing.T) {
        const x int64 = 0x7654321076543210 // 63 bits needed
        for prec := uint(1); prec <= 63; prec++ {
                f := NewFloat(0, prec, ToZero).SetInt64(x)
-               got := f.Int64()
+               got := f.int64()
                want := x &^ (1<<(63-prec) - 1) // cut off (round to zero) low 63-prec bits
                if got != want {
                        t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want)
@@ -571,17 +579,21 @@ func TestFloatUint64(t *testing.T) {
                out uint64
                acc Accuracy
        }{
-               {"0", 0, Exact},
-               {"-0", 0, Exact},
-               {"-1", 0, Above},
                {"-Inf", 0, Above},
+               {"-1", 0, Above},
                {"-1e-1000", 0, Above},
+               {"-0", 0, Exact},
+               {"0", 0, Exact},
                {"1e-1000", 0, Below},
+               {"1", 1, Exact},
+               {"1.000000000000000000001", 1, Below},
                {"12345.0", 12345, Exact},
-               {"12345.6", 12345, Below},
+               {"12345.000000000000000000001", 12345, Below},
                {"18446744073709551615", 18446744073709551615, Exact},
                {"18446744073709551615.000000000000000000001", math.MaxUint64, Below},
+               {"18446744073709551616", math.MaxUint64, Below},
                {"1e10000", math.MaxUint64, Below},
+               {"+Inf", math.MaxUint64, Below},
        } {
                x := makeFloat(test.x)
                out, acc := x.Uint64()
@@ -591,6 +603,44 @@ func TestFloatUint64(t *testing.T) {
        }
 }
 
+func TestFloatInt64(t *testing.T) {
+       for _, test := range []struct {
+               x   string
+               out int64
+               acc Accuracy
+       }{
+               {"-Inf", math.MinInt64, Above},
+               {"-1e10000", math.MinInt64, Above},
+               {"-9223372036854775809", math.MinInt64, Above},
+               {"-9223372036854775808.000000000000000000001", math.MinInt64, Above},
+               {"-9223372036854775808", -9223372036854775808, Exact},
+               {"-9223372036854775807.000000000000000000001", -9223372036854775807, Above},
+               {"-9223372036854775807", -9223372036854775807, Exact},
+               {"-12345.000000000000000000001", -12345, Above},
+               {"-12345.0", -12345, Exact},
+               {"-1.000000000000000000001", -1, Above},
+               {"-1", -1, Exact},
+               {"-1e-1000", 0, Above},
+               {"0", 0, Exact},
+               {"1e-1000", 0, Below},
+               {"1", 1, Exact},
+               {"1.000000000000000000001", 1, Below},
+               {"12345.0", 12345, Exact},
+               {"12345.000000000000000000001", 12345, Below},
+               {"9223372036854775807", 9223372036854775807, Exact},
+               {"9223372036854775807.000000000000000000001", math.MaxInt64, Below},
+               {"9223372036854775808", math.MaxInt64, Below},
+               {"1e10000", math.MaxInt64, Below},
+               {"+Inf", math.MaxInt64, Below},
+       } {
+               x := makeFloat(test.x)
+               out, acc := x.Int64()
+               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