Exact remainders; refuse numbers held rounded (closes #3)
check / check (push) Successful in 1m14s
check / check (push) Successful in 1m14s
x % y is now y times the fractional part of x/y. The old form, x minus y times the whole part of x/y, passed through a product that go/constant could hold only rounded even when both operands and the remainder were exact, and then replied 0. A number whose numerator or denominator reaches 4096 bits, which go/constant holds rounded, is now refused as too large wherever it appears, literals included. A sum of such numbers could lose the answer, and a rounded exponent near a whole number was computed as an exact power. This replaces the separate check on a negative base's exponent. Model: opus-5-5
This commit is contained in:
+25
-25
@@ -246,8 +246,8 @@ func number(tok string) (constant.Value, error) {
|
||||
v := constant.MakeFromLiteral(tok, token.FLOAT, 0)
|
||||
|
||||
// The syntax was checked above, so Unknown here means the exponent
|
||||
// overflowed.
|
||||
if v.Kind() == constant.Unknown {
|
||||
// overflowed. A literal such as 1e1300 is held rounded: see rounded.
|
||||
if v.Kind() == constant.Unknown || rounded(v) {
|
||||
return nil, ErrTooLarge
|
||||
}
|
||||
|
||||
@@ -282,8 +282,9 @@ func apply(x constant.Value, op string, y constant.Value) (constant.Value, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// go/constant represents an overflow to infinity as Unknown.
|
||||
if v.Kind() == constant.Unknown {
|
||||
// go/constant represents an overflow to infinity as Unknown. A
|
||||
// rounded number is refused too: see rounded.
|
||||
if v.Kind() == constant.Unknown || rounded(v) {
|
||||
return nil, ErrTooLarge
|
||||
}
|
||||
|
||||
@@ -309,22 +310,28 @@ func modulo(x, y constant.Value) (constant.Value, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The whole part of a rounded quotient, and so the remainder, would
|
||||
// be wrong.
|
||||
// The fractional part of a rounded quotient, and so the remainder,
|
||||
// would be wrong.
|
||||
if rounded(q) {
|
||||
return nil, ErrTooLarge
|
||||
}
|
||||
|
||||
// token.QUO_ASSIGN is go/constant's integer division, which
|
||||
// truncates, so r has the sign of x and is less than y in size.
|
||||
whole := constant.BinaryOp(constant.Num(q), token.QUO_ASSIGN, constant.Denom(q))
|
||||
r := constant.BinaryOp(x, token.SUB, constant.BinaryOp(y, token.MUL, whole))
|
||||
// x % y is y times the fractional part of x/y, which is at least 0
|
||||
// and less than 1, so the result has the sign of y. It is not
|
||||
// computed as x minus y times the whole part of x/y: that product
|
||||
// can be too large to hold exactly when the remainder is not.
|
||||
//
|
||||
// For x/y = n/d the fractional part is (n mod d)/d, exact because d
|
||||
// is. token.REM truncates, leaving the sign of n; adding d brings a
|
||||
// negative one into range.
|
||||
n, d := constant.Num(q), constant.Denom(q)
|
||||
|
||||
if constant.Sign(r) != 0 && constant.Sign(r) != constant.Sign(y) {
|
||||
r = constant.BinaryOp(r, token.ADD, y)
|
||||
m := constant.BinaryOp(n, token.REM, d)
|
||||
if constant.Sign(m) < 0 {
|
||||
m = constant.BinaryOp(m, token.ADD, d)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
return constant.BinaryOp(y, token.MUL, constant.BinaryOp(m, token.QUO, d)), nil
|
||||
}
|
||||
|
||||
// power computes x^y. A negative x needs a whole y, and its sign is
|
||||
@@ -339,9 +346,6 @@ func power(x, y constant.Value) (constant.Value, error) {
|
||||
return nil, ErrDivisionByZero
|
||||
case constant.Sign(x) >= 0:
|
||||
return nonNegativePower(x, y, n), nil
|
||||
case rounded(y):
|
||||
// Whether a rounded y is whole, or odd, is unknown.
|
||||
return nil, ErrTooLarge
|
||||
case n.Kind() != constant.Int:
|
||||
return nil, ErrNoRealResult
|
||||
}
|
||||
@@ -381,14 +385,7 @@ func exactPowerFits(x constant.Value, e int64) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Num and Denom are Unknown for a value too large or too small to
|
||||
// be held as a fraction.
|
||||
num, den := constant.Num(x), constant.Denom(x)
|
||||
if num.Kind() != constant.Int {
|
||||
return false
|
||||
}
|
||||
|
||||
bits := int64(constant.BitLen(num) + constant.BitLen(den))
|
||||
bits := int64(constant.BitLen(constant.Num(x)) + constant.BitLen(constant.Denom(x)))
|
||||
|
||||
return bits*max(e, -e) <= maxExactPowerBits
|
||||
}
|
||||
@@ -416,7 +413,10 @@ func exactPower(x constant.Value, e int64) constant.Value {
|
||||
// rounded reports whether go/constant holds v rounded. It holds a
|
||||
// number exactly, as a fraction, only while the numerator and the
|
||||
// denominator each stay under 4096 bits; past that, and for a literal of
|
||||
// that size, it holds a 512-bit float.
|
||||
// that size, it holds a 512-bit float. Such a number is refused as too
|
||||
// large wherever it appears: a sum can lose the answer entirely
|
||||
// (7^1000*7^1000 + 5 - 7^1000*7^1000 would be 0), and a remainder, or
|
||||
// whether an exponent is whole or odd, cannot be read from one.
|
||||
func rounded(v constant.Value) bool {
|
||||
_, isFloat := constant.Val(v).(*big.Float)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user