From: Robert Griesemer Date: Wed, 11 Feb 2015 19:22:45 +0000 (-0800) Subject: math/big: implemented Float.Int64, simplified Float.Uint64 X-Git-Tag: go1.5beta1~2030 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=9e9ddb004f6d33305765b446b0425f19ea6e12bf;p=gostls13.git math/big: implemented Float.Int64, simplified Float.Uint64 Change-Id: Ic270ffa7ec6f6dd4b0a951c64ad965447cce1417 Reviewed-on: https://go-review.googlesource.com/4571 Reviewed-by: Alan Donovan --- diff --git a/src/math/big/float.go b/src/math/big/float.go index 3464192aee..06b3ae2f40 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -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 { diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go index 3ec8e83131..dd059ba4a5 100644 --- a/src/math/big/float_test.go +++ b/src/math/big/float_test.go @@ -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