mirror of
https://github.com/golang/go
synced 2024-11-17 18:44:44 -07:00
[dev.typeparams] go/constant: choose internal float representations more consistently
go/constant represents a Float constant either as a rational number (if numerator and denominator are small enough), or, as a "catch-all", as a arbitrary-precision floating-point number. This CL cleans up some of these transitions by factoring out more of the decision logic and documents the rationale between the state transitions better. This CL also simplifies some unrelated code that was overly complex. Updates #43908. Change-Id: Iccdd2d6b7fb7ed13d68ed5e6d992d1bc56a065bb Reviewed-on: https://go-review.googlesource.com/c/go/+/286572 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
34704e374f
commit
d39685e5e9
@ -72,6 +72,17 @@ const prec = 512
|
|||||||
// too large (incl. infinity), that could be recorded in unknownVal.
|
// too large (incl. infinity), that could be recorded in unknownVal.
|
||||||
// See also #20583 and #42695 for use cases.
|
// 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 (
|
type (
|
||||||
unknownVal struct{}
|
unknownVal struct{}
|
||||||
boolVal bool
|
boolVal bool
|
||||||
@ -263,13 +274,7 @@ func i64tor(x int64Val) ratVal { return ratVal{newRat().SetInt64(int64(x))} }
|
|||||||
func i64tof(x int64Val) floatVal { return floatVal{newFloat().SetInt64(int64(x))} }
|
func i64tof(x int64Val) floatVal { return floatVal{newFloat().SetInt64(int64(x))} }
|
||||||
func itor(x intVal) ratVal { return ratVal{newRat().SetInt(x.val)} }
|
func itor(x intVal) ratVal { return ratVal{newRat().SetInt(x.val)} }
|
||||||
func itof(x intVal) floatVal { return floatVal{newFloat().SetInt(x.val)} }
|
func itof(x intVal) floatVal { return floatVal{newFloat().SetInt(x.val)} }
|
||||||
|
func rtof(x ratVal) floatVal { return floatVal{newFloat().SetRat(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 vtoc(x Value) complexVal { return complexVal{x, int64Val(0)} }
|
||||||
|
|
||||||
func makeInt(x *big.Int) Value {
|
func makeInt(x *big.Int) Value {
|
||||||
@ -279,21 +284,15 @@ func makeInt(x *big.Int) Value {
|
|||||||
return intVal{x}
|
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 {
|
func makeRat(x *big.Rat) Value {
|
||||||
a := x.Num()
|
a := x.Num()
|
||||||
b := x.Denom()
|
b := x.Denom()
|
||||||
if a.BitLen() < maxExp && b.BitLen() < maxExp {
|
if smallInt(a) && smallInt(b) {
|
||||||
// ok to remain fraction
|
// ok to remain fraction
|
||||||
return ratVal{x}
|
return ratVal{x}
|
||||||
}
|
}
|
||||||
// components too large => switch to float
|
// components too large => switch to float
|
||||||
fa := newFloat().SetInt(a)
|
return floatVal{newFloat().SetRat(x)}
|
||||||
fb := newFloat().SetInt(b)
|
|
||||||
return floatVal{fa.Quo(fa, fb)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var floatVal0 = floatVal{newFloat()}
|
var floatVal0 = floatVal{newFloat()}
|
||||||
@ -306,6 +305,9 @@ func makeFloat(x *big.Float) Value {
|
|||||||
if x.IsInf() {
|
if x.IsInf() {
|
||||||
return unknownVal{}
|
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}
|
return floatVal{x}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +320,7 @@ func makeComplex(re, im Value) Value {
|
|||||||
|
|
||||||
func makeFloatFromLiteral(lit string) Value {
|
func makeFloatFromLiteral(lit string) Value {
|
||||||
if f, ok := newFloat().SetString(lit); ok {
|
if f, ok := newFloat().SetString(lit); ok {
|
||||||
if smallRat(f) {
|
if smallFloat(f) {
|
||||||
// ok to use rationals
|
// ok to use rationals
|
||||||
if f.Sign() == 0 {
|
if f.Sign() == 0 {
|
||||||
// Issue 20228: If the float underflowed to zero, parse just "0".
|
// Issue 20228: If the float underflowed to zero, parse just "0".
|
||||||
@ -337,15 +339,35 @@ func makeFloatFromLiteral(lit string) Value {
|
|||||||
return nil
|
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.
|
// if converted to a *big.Rat.
|
||||||
func smallRat(x *big.Float) bool {
|
func smallInt(x *big.Int) bool {
|
||||||
if !x.IsInf() {
|
return x.BitLen() < maxExp
|
||||||
e := x.MantExp(nil)
|
}
|
||||||
|
|
||||||
|
// smallFloat64 reports whether x would lead to "reasonably"-sized fraction
|
||||||
|
// if converted to a *big.Rat.
|
||||||
|
func smallFloat64(x float64) bool {
|
||||||
|
if math.IsInf(x, 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, e := math.Frexp(x)
|
||||||
return -maxExp < e && e < maxExp
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Factories
|
// Factories
|
||||||
@ -377,8 +399,11 @@ func MakeFloat64(x float64) Value {
|
|||||||
if math.IsInf(x, 0) || math.IsNaN(x) {
|
if math.IsInf(x, 0) || math.IsNaN(x) {
|
||||||
return unknownVal{}
|
return unknownVal{}
|
||||||
}
|
}
|
||||||
|
if smallFloat64(x) {
|
||||||
return ratVal{newRat().SetFloat64(x + 0)} // convert -0 to 0
|
return ratVal{newRat().SetFloat64(x + 0)} // convert -0 to 0
|
||||||
}
|
}
|
||||||
|
return floatVal{newFloat().SetFloat64(x + 0)}
|
||||||
|
}
|
||||||
|
|
||||||
// MakeFromLiteral returns the corresponding integer, floating-point,
|
// MakeFromLiteral returns the corresponding integer, floating-point,
|
||||||
// imaginary, character, or string value for a Go literal string. The
|
// imaginary, character, or string value for a Go literal string. The
|
||||||
@ -733,7 +758,7 @@ func Num(x Value) Value {
|
|||||||
case ratVal:
|
case ratVal:
|
||||||
return makeInt(x.val.Num())
|
return makeInt(x.val.Num())
|
||||||
case floatVal:
|
case floatVal:
|
||||||
if smallRat(x.val) {
|
if smallFloat(x.val) {
|
||||||
r, _ := x.val.Rat(nil)
|
r, _ := x.val.Rat(nil)
|
||||||
return makeInt(r.Num())
|
return makeInt(r.Num())
|
||||||
}
|
}
|
||||||
@ -755,7 +780,7 @@ func Denom(x Value) Value {
|
|||||||
case ratVal:
|
case ratVal:
|
||||||
return makeInt(x.val.Denom())
|
return makeInt(x.val.Denom())
|
||||||
case floatVal:
|
case floatVal:
|
||||||
if smallRat(x.val) {
|
if smallFloat(x.val) {
|
||||||
r, _ := x.val.Rat(nil)
|
r, _ := x.val.Rat(nil)
|
||||||
return makeInt(r.Denom())
|
return makeInt(r.Denom())
|
||||||
}
|
}
|
||||||
@ -828,7 +853,7 @@ func ToInt(x Value) Value {
|
|||||||
// avoid creation of huge integers
|
// avoid creation of huge integers
|
||||||
// (Existing tests require permitting exponents of at least 1024;
|
// (Existing tests require permitting exponents of at least 1024;
|
||||||
// allow any value that would also be permissible as a fraction.)
|
// allow any value that would also be permissible as a fraction.)
|
||||||
if smallRat(x.val) {
|
if smallFloat(x.val) {
|
||||||
i := newInt()
|
i := newInt()
|
||||||
if _, acc := x.val.Int(i); acc == big.Exact {
|
if _, acc := x.val.Int(i); acc == big.Exact {
|
||||||
return makeInt(i)
|
return makeInt(i)
|
||||||
@ -871,14 +896,16 @@ func ToInt(x Value) Value {
|
|||||||
func ToFloat(x Value) Value {
|
func ToFloat(x Value) Value {
|
||||||
switch x := x.(type) {
|
switch x := x.(type) {
|
||||||
case int64Val:
|
case int64Val:
|
||||||
return i64tor(x)
|
return i64tor(x) // x is always a small int
|
||||||
case intVal:
|
case intVal:
|
||||||
|
if smallInt(x.val) {
|
||||||
return itor(x)
|
return itor(x)
|
||||||
|
}
|
||||||
|
return itof(x)
|
||||||
case ratVal, floatVal:
|
case ratVal, floatVal:
|
||||||
return x
|
return x
|
||||||
case complexVal:
|
case complexVal:
|
||||||
if im := ToInt(x.im); im.Kind() == Int && Sign(im) == 0 {
|
if Sign(x.im) == 0 {
|
||||||
// imaginary component is 0
|
|
||||||
return ToFloat(x.re)
|
return ToFloat(x.re)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -889,13 +916,7 @@ func ToFloat(x Value) Value {
|
|||||||
// Otherwise it returns an Unknown.
|
// Otherwise it returns an Unknown.
|
||||||
func ToComplex(x Value) Value {
|
func ToComplex(x Value) Value {
|
||||||
switch x := x.(type) {
|
switch x := x.(type) {
|
||||||
case int64Val:
|
case int64Val, intVal, ratVal, floatVal:
|
||||||
return vtoc(i64tof(x))
|
|
||||||
case intVal:
|
|
||||||
return vtoc(itof(x))
|
|
||||||
case ratVal:
|
|
||||||
return vtoc(x)
|
|
||||||
case floatVal:
|
|
||||||
return vtoc(x)
|
return vtoc(x)
|
||||||
case complexVal:
|
case complexVal:
|
||||||
return x
|
return x
|
||||||
|
Loading…
Reference in New Issue
Block a user