Files
scriptc/tests/corpus/7983-integer-compares.ts
T
Chris Tate c98c1fe345 Test powers of two exactly before treating a product as exact
The product of two exact integers was treated as exact everywhere when one
of them was a constant power of two, tested with Number.isInteger(Math.log2).
Math.log2 rounds to 52 for 2^52 - 1, so a product by 2^52 - 1 that passes
2^53 was compared as its integer instead of its rounded double. The test now
compares 2 ** Math.round(Math.log2(value)) with the value. Corpus 7983 adds
products by 2^52 - 1 compared with products by 4.
2026-10-24 16:15:53 -05:00

167 lines
5.7 KiB
TypeScript

// Compares of integer values that are not doubles in the generated code:
// typed-array elements and char codes, which read as NaN past the end, and
// sums such as `2 * i + 1` whose double is the integer rounded once, near and
// past 2^53.
function countPairs(a: Int32Array): string {
// `j` runs one past the end, where `a[j]` is NaN and every order compare
// is false.
let lt = 0;
let ge = 0;
let eq = 0;
let ne = 0;
for (let i = 0; i < a.length; i++) {
for (let j = 0; j <= a.length; j++) {
if (a[i]! < a[j]!) lt++;
if (a[i]! >= a[j]!) ge++;
if (a[i]! === a[j]!) eq++;
if (a[i]! !== a[j]!) ne++;
}
}
return `${lt} ${ge} ${eq} ${ne}`;
}
function pastTheEnd(a: Int32Array, b: Uint8Array, c: Uint32Array): string {
const n = a.length;
const out: boolean[] = [];
out.push(a[n]! < a[0]!, a[n]! > a[0]!, a[n]! <= a[n]!, a[n]! === a[0]!, a[0]! !== a[n]!);
out.push(a[n]! < 0, a[n]! >= -1, a[-1]! < a[0]!, b[n]! <= b[0]!, b[0]! !== b[n]!);
out.push(c[n]! < c[0]!, c[n]! >= c[n]!, c[0]! === c[0]!, c[n]! !== c[0]!);
out.push(a[0]! < b[n]!, b[0]! >= a[n]!, a[0]! < c[n]!);
return out.map((v) => (v ? 1 : 0)).join("");
}
function unsigned(c: Uint32Array): string {
// Elements above 2^31 compare as unsigned values.
let below = 0;
let above = 0;
let same = 0;
for (let i = 0; i < c.length; i++) {
for (let j = 0; j < c.length; j++) {
if (c[i]! < c[j]!) below++;
if (c[i]! > c[j]!) above++;
if (c[i]! === c[j]!) same++;
}
}
return `${below} ${above} ${same}`;
}
function mixed(a: Int32Array, b: Uint8Array, c: Uint32Array, d: Int16Array): string {
let count = 0;
for (let i = 0; i < a.length; i++) {
if (a[i]! < b[i]!) count += 1;
if (a[i]! < c[i]!) count += 10;
if (c[i]! > d[i]!) count += 100;
if (b[i]! >= d[i]!) count += 1000;
if (a[i]! === c[i]!) count += 10000;
}
return String(count);
}
function charCodes(s: string): string {
// `s.charCodeAt(s.length)` is NaN.
let rising = 0;
let falls = 0;
let equal = 0;
let differ = 0;
for (let i = 0; i < s.length; i++) {
if (s.charCodeAt(i) < s.charCodeAt(i + 1)) rising++;
if (s.charCodeAt(i) >= s.charCodeAt(i + 1)) falls++;
if (s.charCodeAt(i) === s.charCodeAt(i + 1)) equal++;
if (s.charCodeAt(i) !== s.charCodeAt(i + 1)) differ++;
}
return `${rising} ${falls} ${equal} ${differ}`;
}
function heapChildren(start: number, end: number, steps: number): string {
// `2 * i + 1` passes 2^53 for `i` at or above 2^52, where its double
// rounds; `end` stays a safe integer.
const out: string[] = [];
for (let i = start; i < start + steps; i++) {
const child = 2 * i + 1;
out.push(
[2 * i + 1 < end, 2 * i + 1 <= end, 2 * i + 1 > end, 2 * i + 1 === end, 2 * i + 1 !== end]
.map((v) => (v ? 1 : 0))
.join("") + ` ${child}`,
);
}
return out.join(" ");
}
function countNear(start: number, end: number, steps: number): string {
// The same compares in a loop over integer locals only, stopping early if
// `i` reaches `end`.
let lt = 0;
let le = 0;
let gt = 0;
let eq = 0;
let ne = 0;
for (let i = start; i < start + steps && i !== end; i++) {
if (2 * i + 1 < end) lt++;
if (2 * i + 1 <= end) le++;
if (2 * i + 1 > end) gt++;
if (2 * i + 1 === end) eq++;
if (2 * i + 1 !== end) ne++;
}
return `${lt} ${le} ${gt} ${eq} ${ne}`;
}
function descend(root: number, end: number): number {
// A sift-down's index walk: doubles `root` until `2 * root + 1` reaches `end`.
let steps = 0;
while (2 * root + 1 < end) {
root = 2 * root + 1;
steps++;
}
return steps * 1000 + (root % 1000);
}
function scaled(i: number, end: number): string {
// Products by a power of two never round: 4 * i and 1024 * i compare exactly.
return [4 * i < end, 4 * i === end, 1024 * i - 1 < end, 3 * i + 1 < end, 2 * i - 1 > -end]
.map((v) => (v ? 1 : 0))
.join("");
}
function roundedProducts(): string {
// 3 * (2^52 - 1) is 13510798882111485, which rounds to 13510798882111484 =
// 4 * 3377699720527871: the products' doubles are equal and the integers
// are not. A product by 4 never rounds.
let equal = 0;
let below = 0;
for (let i = 1; i <= 8; i++) {
for (let j = 3377699720527869; j <= 3377699720527873; j++) {
if (i * 4503599627370495 === j * 4) equal++;
if (i * 4503599627370495 < j * 4) below++;
}
}
return `${equal} ${below}`;
}
const a = new Int32Array([5, -3, 2147483647, -2147483648, 5, 0]);
const b = new Uint8Array([0, 255, 7, 128, 5, 0]);
const c = new Uint32Array([4294967295, 2147483648, 2147483647, 0, 1, 4294967295]);
const d = new Int16Array([-32768, 32767, -1, 1, 5, 0]);
console.log(countPairs(a));
console.log(countPairs(new Int32Array(0)));
console.log(pastTheEnd(a, b, c));
console.log(unsigned(c));
console.log(mixed(a, b, c, d));
console.log(charCodes("abcddcbaÿ￿\u0000"));
console.log(charCodes(""));
const p52 = 2 ** 52;
const safe = Number.MAX_SAFE_INTEGER;
console.log(heapChildren(p52 - 2, safe, 5));
console.log(heapChildren(p52 - 2, safe - 1, 5));
console.log(heapChildren(p52 + 100, safe, 3));
console.log(heapChildren(-p52 - 2, -safe, 5));
console.log(heapChildren(0, 4, 4));
console.log(countNear(p52 - 3, safe, 6), countNear(p52 - 3, safe - 2, 6), countNear(p52, safe, 4));
console.log(countNear(-p52 - 3, -safe, 6), countNear(-p52 + 1, -safe + 4, 4), countNear(0, 7, 6));
console.log(descend(0, safe), descend(1, safe), descend(p52 - 1, safe), descend(p52, safe));
console.log(descend(3, 100), descend(0, 1), descend(5, 0));
for (const i of [0, 1, p52 / 2 - 1, p52 / 2, p52 / 1024, p52 / 2 + 1, p52, -p52]) {
console.log(scaled(i, safe), scaled(i, safe - 1), scaled(i, 2 ** 51), scaled(i, 4));
}
console.log(roundedProducts());