// too large (incl. infinity), that could be recorded in unknownVal.
// See also #20583 and #42695 for use cases.
+// Representation of values:
+//
+// Values of Int and Float Kind have two different representations each: int64Val
+// and intVal, and ratVal and floatVal. When possible, the "smaller", respectively
+// more precise (for Floats) representation is chosen. However, once a Float value
+// is represented as a floatVal, any subsequent results remain floatVals (unless
+// explicitly converted); i.e., no attempt is made to convert a floatVal back into
+// a ratVal. The reasoning is that all representations but floatVal are mathematically
+// exact, but once that precision is lost (by moving to floatVal), moving back to
+// a different representation implies a precision that's not actually there.
+
type (
unknownVal struct{}
boolVal bool
func i64tof(x int64Val) floatVal { return floatVal{newFloat().SetInt64(int64(x))} }
func itor(x intVal) ratVal { return ratVal{newRat().SetInt(x.val)} }
func itof(x intVal) floatVal { return floatVal{newFloat().SetInt(x.val)} }
-
-func rtof(x ratVal) floatVal {
- a := newFloat().SetInt(x.val.Num())
- b := newFloat().SetInt(x.val.Denom())
- return floatVal{a.Quo(a, b)}
-}
-
-func vtoc(x Value) complexVal { return complexVal{x, int64Val(0)} }
+func rtof(x ratVal) floatVal { return floatVal{newFloat().SetRat(x.val)} }
+func vtoc(x Value) complexVal { return complexVal{x, int64Val(0)} }
func makeInt(x *big.Int) Value {
if x.IsInt64() {
return intVal{x}
}
-// Permit fractions with component sizes up to maxExp
-// before switching to using floating-point numbers.
-const maxExp = 4 << 10
-
func makeRat(x *big.Rat) Value {
a := x.Num()
b := x.Denom()
- if a.BitLen() < maxExp && b.BitLen() < maxExp {
+ if smallInt(a) && smallInt(b) {
// ok to remain fraction
return ratVal{x}
}
// components too large => switch to float
- fa := newFloat().SetInt(a)
- fb := newFloat().SetInt(b)
- return floatVal{fa.Quo(fa, fb)}
+ return floatVal{newFloat().SetRat(x)}
}
var floatVal0 = floatVal{newFloat()}
if x.IsInf() {
return unknownVal{}
}
+ // No attempt is made to "go back" to ratVal, even if possible,
+ // to avoid providing the illusion of a mathematically exact
+ // representation.
return floatVal{x}
}
func makeFloatFromLiteral(lit string) Value {
if f, ok := newFloat().SetString(lit); ok {
- if smallRat(f) {
+ if smallFloat(f) {
// ok to use rationals
if f.Sign() == 0 {
// Issue 20228: If the float underflowed to zero, parse just "0".
return nil
}
-// smallRat reports whether x would lead to "reasonably"-sized fraction
+// Permit fractions with component sizes up to maxExp
+// before switching to using floating-point numbers.
+const maxExp = 4 << 10
+
+// smallInt reports whether x would lead to "reasonably"-sized fraction
+// if converted to a *big.Rat.
+func smallInt(x *big.Int) bool {
+ return x.BitLen() < maxExp
+}
+
+// smallFloat64 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
+func smallFloat64(x float64) bool {
+ if math.IsInf(x, 0) {
+ return false
+ }
+ _, e := math.Frexp(x)
+ return -maxExp < e && e < maxExp
+}
+
+// smallFloat reports whether x would lead to "reasonably"-sized fraction
+// if converted to a *big.Rat.
+func smallFloat(x *big.Float) bool {
+ if x.IsInf() {
+ return false
}
- return false
+ e := x.MantExp(nil)
+ return -maxExp < e && e < maxExp
}
// ----------------------------------------------------------------------------
if math.IsInf(x, 0) || math.IsNaN(x) {
return unknownVal{}
}
- return ratVal{newRat().SetFloat64(x + 0)} // convert -0 to 0
+ if smallFloat64(x) {
+ return ratVal{newRat().SetFloat64(x + 0)} // convert -0 to 0
+ }
+ return floatVal{newFloat().SetFloat64(x + 0)}
}
// MakeFromLiteral returns the corresponding integer, floating-point,
case ratVal:
return makeInt(x.val.Num())
case floatVal:
- if smallRat(x.val) {
+ if smallFloat(x.val) {
r, _ := x.val.Rat(nil)
return makeInt(r.Num())
}
case ratVal:
return makeInt(x.val.Denom())
case floatVal:
- if smallRat(x.val) {
+ if smallFloat(x.val) {
r, _ := x.val.Rat(nil)
return makeInt(r.Denom())
}
// avoid creation of huge integers
// (Existing tests require permitting exponents of at least 1024;
// allow any value that would also be permissible as a fraction.)
- if smallRat(x.val) {
+ if smallFloat(x.val) {
i := newInt()
if _, acc := x.val.Int(i); acc == big.Exact {
return makeInt(i)
func ToFloat(x Value) Value {
switch x := x.(type) {
case int64Val:
- return i64tor(x)
+ return i64tor(x) // x is always a small int
case intVal:
- return itor(x)
+ if smallInt(x.val) {
+ return itor(x)
+ }
+ return itof(x)
case ratVal, floatVal:
return x
case complexVal:
- if im := ToInt(x.im); im.Kind() == Int && Sign(im) == 0 {
- // imaginary component is 0
+ if Sign(x.im) == 0 {
return ToFloat(x.re)
}
}
// Otherwise it returns an Unknown.
func ToComplex(x Value) Value {
switch x := x.(type) {
- case int64Val:
- return vtoc(i64tof(x))
- case intVal:
- return vtoc(itof(x))
- case ratVal:
- return vtoc(x)
- case floatVal:
+ case int64Val, intVal, ratVal, floatVal:
return vtoc(x)
case complexVal:
return x