]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: implemented Float.Int (truncation of Floats to Ints)
authorRobert Griesemer <gri@golang.org>
Tue, 10 Feb 2015 22:28:25 +0000 (14:28 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 11 Feb 2015 17:02:35 +0000 (17:02 +0000)
Change-Id: Id98f7333fe6ae1b64e0469c6d01f02360c1f8f55
Reviewed-on: https://go-review.googlesource.com/4481
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/float.go
src/math/big/float_test.go

index a5c05499485933928acbb671f57efefeb7c22300..a8a1eead6ae8630687b4699611184313c0718038 100644 (file)
@@ -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 {
index c00aa9d97e69f947d56eace8f8b12208ad286021..8e6490e15d75dc9c3302ba80dd4c6225c850a098 100644 (file)
@@ -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