Commit 3cc24aa9 authored by Robert Griesemer's avatar Robert Griesemer

go/constant: use Float.Rat method instead of doing it manually

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: default avatarAlan Donovan <adonovan@google.com>
parent 57c81ef2
......@@ -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)
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment