diff --git a/src/math/big/float.go b/src/math/big/float.go index a5c05499485..a8a1eead6ae 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -22,7 +22,7 @@ const debugFloat = true // enable for debugging // A Float represents a multi-precision floating point number of the form // -// sign * mantissa * 2**exponent +// sign × mantissa × 2**exponent // // with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp (with the // exception of 0 and Inf which have a 0 mantissa and special exponents). @@ -231,21 +231,18 @@ func (z *Float) SetMantExp(mant *Float, exp int) *Float { // IsInt reports whether x is an integer. // ±Inf are not considered integers. func (x *Float) IsInt() bool { - // pick off easy cases - if len(x.mant) == 0 { - return x.exp != infExp // x == 0 + if debugFloat { + x.validate() } - // x != 0 + // pick off easy cases if x.exp <= 0 { - return false // 0 < |x| <= 0.5 + // |x| < 1 || |x| == Inf + 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 } - if debugFloat { - x.validate() - } // x.mant[len(x.mant)-1] != 0 // determine minimum required precision for x minPrec := uint(len(x.mant))*_W - x.mant.trailingZeroBits() @@ -264,6 +261,9 @@ func (x *Float) IsInf(sign int) bool { // If the exponent's magnitude is too large, z becomes ±Inf. func (z *Float) setExp(e int64) { if -MaxExp <= e && e <= MaxExp { + if len(z.mant) == 0 { + e = 0 + } z.exp = int32(e) return } @@ -706,9 +706,50 @@ func (x *Float) Float64() (float64, Accuracy) { return math.Float64frombits(s | e<<52 | m), r.acc } -// BUG(gri) Int is not yet implemented -func (x *Float) Int() (*Int, Accuracy) { - panic("unimplemented") +// Int returns the result of truncating x towards zero; or nil +// if x is an infinity. The result is Exact if x.IsInt(); +// otherwise it is Below for x > 0, and Above for x < 0. +func (x *Float) Int() (res *Int, acc Accuracy) { + if debugFloat { + x.validate() + } + // accuracy for inexact results + acc = Below // truncation + if x.neg { + acc = Above + } + // pick off easy cases + if x.exp <= 0 { + // |x| < 1 || |x| == Inf + if x.exp == infExp { + return nil, acc // ±Inf + } + if len(x.mant) == 0 { + acc = Exact // ±0 + } + return new(Int), acc // ±0.xxx + } + // x.exp > 0 + // 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 { + acc = Exact + } + // shift mantissa as needed + res = &Int{neg: x.neg} + // TODO(gri) should have a shift that takes positive and negative shift counts + switch { + case exp > allBits: + res.abs = res.abs.shl(x.mant, exp-allBits) + default: + res.abs = res.abs.set(x.mant) + case exp < allBits: + res.abs = res.abs.shr(x.mant, allBits-exp) + } + return } // BUG(gri) Rat is not yet implemented @@ -1042,7 +1083,6 @@ func (z *Float) Mul(x, y *Float) *Float { } // Quo sets z to the rounded quotient x/y and returns z. -// If y == 0, a division-by-zero run-time panic occurs. TODO(gri) this should become Inf // Precision, rounding, and accuracy reporting are as for Add. func (z *Float) Quo(x, y *Float) *Float { if z.prec == 0 { diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go index c00aa9d97e6..8e6490e15d7 100644 --- a/src/math/big/float_test.go +++ b/src/math/big/float_test.go @@ -75,7 +75,7 @@ func makeFloat(s string) *Float { return NewInf(-1) } var x Float - x.prec = 100 // TODO(gri) find a better way to do this + x.prec = 1000 // TODO(gri) find a better way to do this if _, ok := x.SetString(s); !ok { panic(fmt.Sprintf("%q is not a valid float", s)) } @@ -553,6 +553,46 @@ func TestFloatSetRat(t *testing.T) { } } +func TestFloatInt(t *testing.T) { + for _, test := range []struct { + x string + out string + acc Accuracy + }{ + {"0", "0", Exact}, + {"+0", "0", Exact}, + {"-0", "0", Exact}, + {"Inf", "nil", Below}, + {"+Inf", "nil", Below}, + {"-Inf", "nil", Above}, + {"1", "1", Exact}, + {"-1", "-1", Exact}, + {"1.23", "1", Below}, + {"-1.23", "-1", Above}, + {"123e-2", "1", Below}, + {"123e-3", "0", Below}, + {"123e-4", "0", Below}, + {"1e-1000", "0", Below}, + {"-1e-1000", "0", Above}, + {"1e+10", "10000000000", Exact}, + {"1e+100", "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Exact}, + } { + x := makeFloat(test.x) + out, acc := x.Int() + got := "nil" + if out != nil { + got = out.String() + } + if got != test.out || acc != test.acc { + t.Errorf("%s: got %s (%s); want %s (%s)", test.x, got, acc, test.out, test.acc) + } + } +} + +func TestFloatRat(t *testing.T) { + // TODO(gri) implement this +} + // Selected precisions with which to run various tests. var precList = [...]uint{1, 2, 5, 8, 10, 16, 23, 24, 32, 50, 53, 64, 100, 128, 500, 511, 512, 513, 1000, 10000} @@ -678,6 +718,7 @@ func TestFloatAdd64(t *testing.T) { } func TestFloatMul(t *testing.T) { + // TODO(gri) implement this } // TestFloatMul64 tests that Float.Mul/Quo of numbers with