]> Cypherpunks repositories - gostls13.git/commitdiff
go/constant: use Float.Rat method instead of doing it manually
authorRobert Griesemer <gri@golang.org>
Tue, 15 Dec 2015 22:17:56 +0000 (14:17 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 16 Dec 2015 00:24:11 +0000 (00:24 +0000)
Also fixed conversion bug and added corresponding test case.

Change-Id: I26f143fbc8d40a6d073ecb095e61b461495f3d68
Reviewed-on: https://go-review.googlesource.com/17872
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
src/go/constant/value.go
src/go/constant/value_test.go

index 04a2ac981efb5254e6ebb5cd0a70aac290e350ec..630581047a203f01d6e6c6af3a1b9f543d317a04 100644 (file)
@@ -249,10 +249,10 @@ func makeComplex(re, im Value) Value {
 
 func makeFloatFromLiteral(lit string) Value {
        if f, ok := newFloat().SetString(lit); ok {
-               if f.MantExp(nil) < maxExp {
+               if smallRat(f) {
                        // ok to use rationals
                        r, _ := newRat().SetString(lit)
-                       return makeRat(r)
+                       return ratVal{r}
                }
                // otherwise use floats
                return makeFloat(f)
@@ -260,6 +260,16 @@ func makeFloatFromLiteral(lit string) Value {
        return nil
 }
 
+// smallRat reports whether x would lead to "reasonably"-sized fraction
+// if converted to a *big.Rat.
+func smallRat(x *big.Float) bool {
+       if !x.IsInf() {
+               e := x.MantExp(nil)
+               return -maxExp < e && e < maxExp
+       }
+       return false
+}
+
 // ----------------------------------------------------------------------------
 // Factories
 
@@ -572,35 +582,6 @@ func MakeFromBytes(bytes []byte) Value {
        return makeInt(newInt().SetBits(words[:i]))
 }
 
-// toRat returns the fraction corresponding to x, or nil
-// if x cannot be represented as a fraction a/b because
-// its components a or b are too large.
-func toRat(x *big.Float) *big.Rat {
-       m := newFloat()
-       e := x.MantExp(m)
-
-       // fail to convert if fraction components are too large
-       if e <= maxExp || e >= maxExp {
-               return nil
-       }
-
-       // convert mantissa to big.Int value by shifting by ecorr
-       ecorr := int(m.MinPrec())
-       a, _ := m.SetMantExp(m, ecorr).Int(nil)
-       e -= ecorr // correct exponent
-
-       // compute actual fraction
-       b := big.NewInt(1)
-       switch {
-       case e < 0:
-               b.Lsh(b, uint(-e))
-       case e > 0:
-               a.Lsh(a, uint(e))
-       }
-
-       return new(big.Rat).SetFrac(a, b)
-}
-
 // Num returns the numerator of x; x must be Int, Float, or Unknown.
 // If x is Unknown, or if it is too large or small to represent as a
 // fraction, the result is Unknown. Otherwise the result is an Int
@@ -612,7 +593,8 @@ func Num(x Value) Value {
        case ratVal:
                return makeInt(x.val.Num())
        case floatVal:
-               if r := toRat(x.val); r != nil {
+               if smallRat(x.val) {
+                       r, _ := x.val.Rat(nil)
                        return makeInt(r.Num())
                }
        case unknownVal:
@@ -633,7 +615,8 @@ func Denom(x Value) Value {
        case ratVal:
                return makeInt(x.val.Denom())
        case floatVal:
-               if r := toRat(x.val); r != nil {
+               if smallRat(x.val) {
+                       r, _ := x.val.Rat(nil)
                        return makeInt(r.Denom())
                }
        case unknownVal:
@@ -703,8 +686,9 @@ func ToInt(x Value) Value {
 
        case floatVal:
                // avoid creation of huge integers
-               // (existing tests require permitting exponents of at least 1024)
-               if x.val.MantExp(nil) <= 1024 {
+               // (Existing tests require permitting exponents of at least 1024;
+               // allow any value that would also be permissible as a fraction.)
+               if smallRat(x.val) {
                        i := newInt()
                        if _, acc := x.val.Int(i); acc == big.Exact {
                                return makeInt(i)
index 932287ffefd7bfbd5e99aed7b29da377ea0c24ea..de1ab0267a45f4505b7b3235ba0777bbadf08097 100644 (file)
@@ -240,6 +240,7 @@ var stringTests = []struct {
        {"2.1", "2.1", "21/10"},
        {"-2.1", "-2.1", "-21/10"},
        {"1e9999", "1e+9999", "0x.f8d4a9da224650a8cb2959e10d985ad92adbd44c62917e608b1f24c0e1b76b6f61edffeb15c135a4b601637315f7662f325f82325422b244286a07663c9415d2p+33216"},
+       {"1e-9999", "1e-9999", "0x.83b01ba6d8c0425eec1b21e96f7742d63c2653ed0a024cf8a2f9686df578d7b07d7a83d84df6a2ec70a921d1f6cd5574893a7eda4d28ee719e13a5dce2700759p-33215"},
        {"2.71828182845904523536028747135266249775724709369995957496696763", "2.71828", "271828182845904523536028747135266249775724709369995957496696763/100000000000000000000000000000000000000000000000000000000000000"},
 
        // Complex