Require integer views to stay below 2^53 exactly, and fix corpus 1420

This commit is contained in:
Chris Tate
2026-10-23 18:05:10 -05:00
parent 7610edcfa0
commit 39bb8a5b7e
2 changed files with 35 additions and 3 deletions
@@ -65,6 +65,22 @@ function magnitude(range: IntegerRange): number {
return Math.max(Math.abs(range.min), Math.abs(range.max));
}
/** The bounds of a sum and a product of two magnitude bounds, as doubles.
* Below 2^53 the double result is the integer; above it the result is past
* every exactness limit either way, and rounding moves it far less than the
* margin from WIDE_BOUND to the i64 range. A result of exactly 2^53 may
* stand for 2^53 + 1 (2^53 - 1 + 2 rounds to 2^53), so it moves to the next
* double up unless the sum is exactly 2^53. */
function sumBound(a: number, b: number): number {
const sum = a + b;
return sum === EXACT_DOUBLES && EXACT_DOUBLES - a !== b ? sum + 2 : sum;
}
function productBound(a: number, b: number): number {
const product = a * b;
return product === EXACT_DOUBLES ? product + 2 : product;
}
/** The wide view of `l op r` for `+`, `-` and `*` when the exact operands
* have no proven result range (their product or sum may pass 2^53).
*
@@ -98,14 +114,14 @@ export function wideArithmetic(
if (op === "*") {
if (lv.below !== Infinity || rv.below !== Infinity) return null;
below = EXACT_DOUBLES;
bound = lv.bound * rv.bound;
bound = productBound(lv.bound, rv.bound);
} else if (lv.below === Infinity && rv.below === Infinity) {
below = EXACT_DOUBLES;
bound = lv.bound + rv.bound;
bound = sumBound(lv.bound, rv.bound);
} else if (lv.below === Infinity || rv.below === Infinity) {
const [exact, wide] = lv.below === Infinity ? [lv, rv] : [rv, lv];
below = Math.min(wide.below - exact.bound, EXACT_DOUBLES);
bound = exact.bound + wide.bound;
bound = sumBound(exact.bound, wide.bound);
} else return null;
if (!(bound <= WIDE_BOUND) || below < INDEX_LIMIT) return null;
const operand = (value: LlValue, integer: LlInteger | null): string =>
@@ -60,6 +60,20 @@ function nearLimit(start: number): string {
return seen.join(" ");
}
function atLimit(start: number, end: number): string {
// A counter of integer locals only, up to 2^53 - 1: `i + 2` may be
// 2^53 + 1, whose double is 2^53, so `i + 1 === i + 2` holds at the end.
let same = 0;
let above = 0;
let mixed = 0;
for (let i = start; i <= end; i++) {
if (i + 1 === i + 2) same++;
if (i + 2 > 9007199254740992) above++;
mixed += (i * 2) % 3;
}
return `${same} ${above} ${mixed}`;
}
console.log(heapSort(new Int32Array([5, -3, 9, 0, 9, 2147483647, -2147483648, 7, 1])));
console.log(heapSort(new Int32Array([])), heapSort(new Int32Array([4])));
console.log(neighbors(new Float64Array([1, 2, 3, 4])));
@@ -67,3 +81,5 @@ const sorted = new Int32Array([-5, -1, 0, 0, 3, 8, 8, 13]);
console.log([-6, -1, 0, 1, 8, 13, 14].map((v) => lowerBound(sorted, v)).join());
console.log(nearLimit(9007199254740988));
console.log(nearLimit(-9007199254740991));
console.log(atLimit(9007199254740988, 9007199254740991));
console.log(atLimit(-9007199254740991, -9007199254740988));