From 3e9e9b48223b2dcabf1d861af9904940bdbff929 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 24 Feb 2015 18:04:40 -0800 Subject: [PATCH] math/big: implemented Float.Rat Change-Id: If516e12d4b5dfb6f9288437d270569f7e4e2a1cd Reviewed-on: https://go-review.googlesource.com/5871 Reviewed-by: Alan Donovan --- src/math/big/float.go | 43 ++++++++++++++++++++++-- src/math/big/float_test.go | 69 +++++++++++++++++++++++++++++++------- 2 files changed, 97 insertions(+), 15 deletions(-) diff --git a/src/math/big/float.go b/src/math/big/float.go index 60e9a2d46db..8fbe1804990 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -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) diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go index f7c243e71aa..69e88c35011 100644 --- a/src/math/big/float_test.go +++ b/src/math/big/float_test.go @@ -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) {