Refuse numbers a double cannot hold; exact powers up to the limit (closes #3)
check / check (push) Successful in 1m4s
check / check (push) Successful in 1m4s
A power computed in float64 is refused unless its base and its result are normal doubles, and so is a result other than zero that is not one: below about 2.2e-308 a double keeps fewer digits, and at 0 or infinity none. A literal that go/constant reads as 0 though it is not, such as 1e-999999999, is refused too. A whole exponent up to 4096 either way is computed exactly and kept when go/constant holds the result exactly, so 2^-1400 is exact. ErrTooLarge becomes ErrOutOfRange, and its reply, "That needs a number too large or too small for me.", is true of both. Model: opus-5-5
This commit is contained in:
+68
-25
@@ -90,9 +90,15 @@ func TestEvaluatePowers(t *testing.T) {
|
||||
"4^0.5": "2",
|
||||
"0^0.5": "0",
|
||||
"2^1023": "8.98846567431158e+307",
|
||||
"2^-1022": "2.2250738585072014e-308",
|
||||
// Past 2^53 a float64 cannot tell odd from even.
|
||||
"(-1)^(2^53 + 1)": "-1",
|
||||
"(-1)^(10^30)": "1",
|
||||
"(-1)^(2^53 + 1)": "-1",
|
||||
"(-1)^(10^30)": "1",
|
||||
"(-1)^-9223372036854775808": "1",
|
||||
// Whole powers beyond the range of a double, held exactly.
|
||||
"2^-1400 * 2^1365 * 2^35": "1",
|
||||
"0.3^900 * 10^470": "0.25652473503365386",
|
||||
"2^1500 / 2^1000": "3.273390607896142e+150",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -148,11 +154,11 @@ func expectResults(t *testing.T, cases map[string]string) {
|
||||
}
|
||||
|
||||
// TestEvaluateRefuses covers what must be answered with an error rather
|
||||
// than a number, and never with a panic.
|
||||
// than a number.
|
||||
func TestEvaluateRefuses(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := map[string]error{
|
||||
expectErrors(t, map[string]error{
|
||||
"": calc.ErrNotArithmetic,
|
||||
" ": calc.ErrNotArithmetic,
|
||||
"hello": calc.ErrNotArithmetic,
|
||||
@@ -193,25 +199,60 @@ func TestEvaluateRefuses(t *testing.T) {
|
||||
"(-2)^0.5": calc.ErrNoRealResult,
|
||||
"(-8)^(1/3)": calc.ErrNoRealResult,
|
||||
"(-1)^-0.5": calc.ErrNoRealResult,
|
||||
"1e400": calc.ErrTooLarge,
|
||||
"1e300 * 1e300": calc.ErrTooLarge,
|
||||
"1e999999999 * 1e999999999": calc.ErrTooLarge,
|
||||
"1 / 1e-400": calc.ErrTooLarge,
|
||||
"2^1024": calc.ErrTooLarge,
|
||||
"2^5000": calc.ErrTooLarge,
|
||||
"(-2)^5001": calc.ErrTooLarge,
|
||||
"0.5^-5000": calc.ErrTooLarge,
|
||||
})
|
||||
}
|
||||
|
||||
// TestEvaluateOutOfRange: a number is held exactly, or computed in
|
||||
// float64 as a normal double, and a result is written as a normal
|
||||
// double. Anything else is refused.
|
||||
func TestEvaluateOutOfRange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expectErrors(t, map[string]error{
|
||||
// Results that are not normal doubles: 2^-1074 would be written
|
||||
// 5e-324.
|
||||
"1e400": calc.ErrOutOfRange,
|
||||
"1e300 * 1e300": calc.ErrOutOfRange,
|
||||
"1e999999999 * 1e999999999": calc.ErrOutOfRange,
|
||||
"1 / 1e-400": calc.ErrOutOfRange,
|
||||
"2^1024": calc.ErrOutOfRange,
|
||||
"2^5000": calc.ErrOutOfRange,
|
||||
"(-2)^5001": calc.ErrOutOfRange,
|
||||
"0.5^-5000": calc.ErrOutOfRange,
|
||||
"2^-1074": calc.ErrOutOfRange,
|
||||
"2^-1400": calc.ErrOutOfRange,
|
||||
"-1e-310": calc.ErrOutOfRange,
|
||||
// Powers computed in float64 whose base or result is not a
|
||||
// normal double, and so has lost digits, or all of them.
|
||||
"2^-1073.5 * 2^1073": calc.ErrOutOfRange,
|
||||
"1e400^-0.001": calc.ErrOutOfRange,
|
||||
"1e-400^0.001": calc.ErrOutOfRange,
|
||||
"1e-310^0.5": calc.ErrOutOfRange,
|
||||
"(0.5^1100)^4 / (0.5^1100)^4": calc.ErrOutOfRange,
|
||||
"(1/3)^1e400": calc.ErrOutOfRange,
|
||||
// go/constant holds numbers of this size rounded. A sum of them
|
||||
// can lose the answer (this one would be 0), and so can a
|
||||
// remainder or the sign of -1 to such a power.
|
||||
"7^1000 * 7^1000 + 5 - 7^1000 * 7^1000": calc.ErrTooLarge,
|
||||
"7^1000 * 7^1000 / 7^1000 % 10": calc.ErrTooLarge,
|
||||
"(-1)^(3^1365 * 3^1365 / 3^1365)": calc.ErrTooLarge,
|
||||
"(-1)^1e1300": calc.ErrTooLarge,
|
||||
"1e-1300": calc.ErrTooLarge,
|
||||
"7^1000 * 7^1000 + 5 - 7^1000 * 7^1000": calc.ErrOutOfRange,
|
||||
"7^1000 * 7^1000 / 7^1000 % 10": calc.ErrOutOfRange,
|
||||
"(-1)^(3^1365 * 3^1365 / 3^1365)": calc.ErrOutOfRange,
|
||||
"(-1)^1e1300": calc.ErrOutOfRange,
|
||||
"1e-1300": calc.ErrOutOfRange,
|
||||
"1e-1300 + 1": calc.ErrOutOfRange,
|
||||
"1e-700 * 1e-700": calc.ErrOutOfRange,
|
||||
"0.1^800 * 0.1^800": calc.ErrOutOfRange,
|
||||
// Both operands are held exactly, but their quotient is not.
|
||||
"3^1365 % 7^-1000": calc.ErrTooLarge,
|
||||
}
|
||||
"3^1365 % 7^-1000": calc.ErrOutOfRange,
|
||||
// go/constant reads this literal as 0.
|
||||
"1e-999999999": calc.ErrOutOfRange,
|
||||
"1 / 1e-999999999": calc.ErrOutOfRange,
|
||||
})
|
||||
}
|
||||
|
||||
// expectErrors checks that each expression is refused with its error,
|
||||
// and never with a panic.
|
||||
func expectErrors(t *testing.T, cases map[string]error) {
|
||||
t.Helper()
|
||||
|
||||
for in, want := range cases {
|
||||
t.Run(in, func(t *testing.T) {
|
||||
@@ -236,15 +277,17 @@ func TestEvaluateBoundsWork(t *testing.T) {
|
||||
want string
|
||||
err error
|
||||
}{
|
||||
{in: "9^9^9^9^9", err: calc.ErrTooLarge},
|
||||
{in: "((9^999)^999)^999", err: calc.ErrTooLarge},
|
||||
{in: "9^9^9^9^9", err: calc.ErrOutOfRange},
|
||||
{in: "((9^999)^999)^999", err: calc.ErrOutOfRange},
|
||||
{in: "(3^2583)^4096", err: calc.ErrOutOfRange},
|
||||
{in: "1.0000001^99999", want: "1.01005006557947"},
|
||||
{in: "0.5^99999999999999999999", want: "0"},
|
||||
{in: "0.5^99999999999999999999", err: calc.ErrOutOfRange},
|
||||
{in: "2^-9223372036854775808", err: calc.ErrOutOfRange},
|
||||
{in: "(-1)^99999999999999999999", want: "-1"},
|
||||
// The longest tower that fits.
|
||||
{in: strings.Repeat("9^", 127) + "9", err: calc.ErrTooLarge},
|
||||
// The largest power computed exactly, as often as fits.
|
||||
{in: "0" + strings.Repeat("*3^1365", 36), want: "0"},
|
||||
{in: strings.Repeat("9^", 127) + "9", err: calc.ErrOutOfRange},
|
||||
// The largest power of 3 computed exactly, as often as fits.
|
||||
{in: "0" + strings.Repeat("*3^2583", 36), want: "0"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
||||
Reference in New Issue
Block a user