]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: implemented Float.Rat
authorRobert Griesemer <gri@golang.org>
Wed, 25 Feb 2015 02:04:40 +0000 (18:04 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 25 Feb 2015 16:53:28 +0000 (16:53 +0000)
Change-Id: If516e12d4b5dfb6f9288437d270569f7e4e2a1cd
Reviewed-on: https://go-review.googlesource.com/5871
Reviewed-by: Alan Donovan <adonovan@google.com>
src/math/big/float.go
src/math/big/float_test.go

index 60e9a2d46db4edb574472a6575429f184246be7d..8fbe180499062df0339eb04d5e8d7320904d1dbc 100644 (file)
@@ -789,6 +789,7 @@ func (x *Float) Float64() (float64, Accuracy) {
 // 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) {
+       // TODO(gri) accept z argument for result storage (see Float.Rat below)
        if debugFloat {
                validate(x)
        }
@@ -830,9 +831,45 @@ func (x *Float) Int() (res *Int, acc Accuracy) {
        return
 }
 
-// BUG(gri) Rat is not yet implemented
-func (x *Float) Rat() *Rat {
-       panic("unimplemented")
+// Rat returns x converted into an exact fraction; or nil if x is an infinity.
+// If a non-nil *Rat argument z is provided, it is used to store the result;
+// otherwise a new Rat is allocated.
+func (x *Float) Rat(z *Rat) *Rat {
+       if debugFloat {
+               validate(x)
+       }
+       // pick off easy cases
+       switch x.ord() {
+       case -2, +2:
+               return nil // ±Inf
+       case 0:
+               if z == nil {
+                       return new(Rat)
+               }
+               return z.SetInt64(0)
+       }
+       // x != 0 && x != ±Inf
+       allBits := int32(len(x.mant)) * _W
+       // build up numerator and denominator
+       if z == nil {
+               z = new(Rat)
+       }
+       z.a.neg = x.neg
+       switch {
+       case x.exp > allBits:
+               z.a.abs = z.a.abs.shl(x.mant, uint(x.exp-allBits))
+               z.b.abs = z.b.abs[:0] // == 1 (see Rat)
+               return z              // already in normal form
+       default:
+               z.a.abs = z.a.abs.set(x.mant)
+               z.b.abs = z.b.abs[:0] // == 1 (see Rat)
+               return z              // already in normal form
+       case x.exp < allBits:
+               z.a.abs = z.a.abs.set(x.mant)
+               t := z.b.abs.setUint64(1)
+               z.b.abs = t.shl(t, uint(allBits-x.exp))
+               return z.norm()
+       }
 }
 
 // Abs sets z to the (possibly rounded) value |x| (the absolute value of x)
index f7c243e71aa7ce5c554ccd4296a78fb8ebe4d990..69e88c3501162826d482c6704e3735f0d844ddfe 100644 (file)
@@ -149,7 +149,7 @@ func TestFloatMantExp(t *testing.T) {
                frac := makeFloat(test.frac)
                f, e := x.MantExp(nil)
                if !feq(f, frac) || e != test.exp {
-                       t.Errorf("%s.MantExp() = %s, %d; want %s, %d", test.x, f.Format('g', 10), e, test.frac, test.exp)
+                       t.Errorf("%s.MantExp(nil) = %s, %d; want %s, %d", test.x, f.Format('g', 10), e, test.frac, test.exp)
                }
        }
 }
@@ -158,10 +158,10 @@ func TestFloatMantExpAliasing(t *testing.T) {
        x := makeFloat("0.5p10")
        z := new(Float)
        if m, _ := x.MantExp(z); m != z {
-               t.Fatalf("MantExp didn't use supplied *Float")
+               t.Fatalf("Float.MantExp didn't use supplied *Float")
        }
        if _, e := x.MantExp(x); e != 10 {
-               t.Fatalf("MantExp aliasing error: got %d; want 10", e)
+               t.Fatalf("Float.MantExp aliasing error: got %d; want 10", e)
        }
 }
 
@@ -691,9 +691,9 @@ func TestFloatInt64(t *testing.T) {
 
 func TestFloatInt(t *testing.T) {
        for _, test := range []struct {
-               x   string
-               out string
-               acc Accuracy
+               x    string
+               want string
+               acc  Accuracy
        }{
                {"0", "0", Exact},
                {"+0", "0", Exact},
@@ -714,19 +714,64 @@ func TestFloatInt(t *testing.T) {
                {"1e+100", "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Exact},
        } {
                x := makeFloat(test.x)
-               out, acc := x.Int()
+               res, acc := x.Int()
                got := "nil"
-               if out != nil {
-                       got = out.String()
+               if res != nil {
+                       got = res.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)
+               if got != test.want || acc != test.acc {
+                       t.Errorf("%s: got %s (%s); want %s (%s)", test.x, got, acc, test.want, test.acc)
                }
        }
 }
 
 func TestFloatRat(t *testing.T) {
-       // TODO(gri) implement this
+       for _, test := range []struct {
+               x, want string
+       }{
+               {"0", "0/1"},
+               {"+0", "0/1"},
+               {"-0", "0/1"},
+               {"Inf", "nil"},
+               {"+Inf", "nil"},
+               {"-Inf", "nil"},
+               {"1", "1/1"},
+               {"-1", "-1/1"},
+               {"1.25", "5/4"},
+               {"-1.25", "-5/4"},
+               {"1e10", "10000000000/1"},
+               {"1p10", "1024/1"},
+               {"-1p-10", "-1/1024"},
+               {"3.14159265", "7244019449799623199/2305843009213693952"},
+       } {
+               x := makeFloat(test.x).SetPrec(64)
+               res := x.Rat(nil)
+               got := "nil"
+               if res != nil {
+                       got = res.String()
+               }
+               if got != test.want {
+                       t.Errorf("%s: got %s; want %s", test.x, got, test.want)
+                       continue
+               }
+
+               // inverse conversion
+               if res != nil {
+                       got := new(Float).SetPrec(64).SetRat(res)
+                       if got.Cmp(x) != 0 {
+                               t.Errorf("%s: got %s; want %s", test.x, got, x)
+                       }
+               }
+       }
+
+       // check that supplied *Rat is used
+       for _, f := range []string{"0", "1"} {
+               x := makeFloat(f)
+               r := new(Rat)
+               if res := x.Rat(r); res != r {
+                       t.Errorf("(%s).Rat is not using supplied *Rat", f)
+               }
+       }
 }
 
 func TestFloatAbs(t *testing.T) {