diff options
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/unittests')
7 files changed, 1442 insertions, 1108 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/unittests/conversion.spec.ts b/dom/webgpu/tests/cts/checkout/src/unittests/conversion.spec.ts index 8606aa8717..e144f39288 100644 --- a/dom/webgpu/tests/cts/checkout/src/unittests/conversion.spec.ts +++ b/dom/webgpu/tests/cts/checkout/src/unittests/conversion.spec.ts @@ -18,7 +18,7 @@ import { i32, kFloat16Format, kFloat32Format, - Matrix, + MatrixValue, numbersApproximatelyEqual, pack2x16float, pack2x16snorm, @@ -26,14 +26,14 @@ import { pack4x8snorm, pack4x8unorm, packRGB9E5UFloat, - Scalar, + ScalarValue, toMatrix, u32, unpackRGB9E5UFloat, vec2, vec3, vec4, - Vector, + VectorValue, } from '../webgpu/util/conversion.js'; import { UnitTest } from './unit_test.js'; @@ -191,7 +191,7 @@ g.test('floatBitsToULPFromZero,32').fn(t => { }); g.test('scalarWGSL').fn(t => { - const cases: Array<[Scalar, string]> = [ + const cases: Array<[ScalarValue, string]> = [ [f32(0.0), '0.0f'], // The number -0.0 can be remapped to 0.0 when stored in a Scalar // object. It is not possible to guarantee that '-0.0f' will @@ -227,7 +227,7 @@ expect: ${expect}` }); g.test('vectorWGSL').fn(t => { - const cases: Array<[Vector, string]> = [ + const cases: Array<[VectorValue, string]> = [ [vec2(f32(42.0), f32(24.0)), 'vec2(42.0f, 24.0f)'], [vec2(f16Bits(0x5140), f16Bits(0x4e00)), 'vec2(42.0h, 24.0h)'], [vec2(u32(42), u32(24)), 'vec2(42u, 24u)'], @@ -261,7 +261,7 @@ expect: ${expect}` }); g.test('matrixWGSL').fn(t => { - const cases: Array<[Matrix, string]> = [ + const cases: Array<[MatrixValue, string]> = [ [ toMatrix( [ @@ -391,7 +391,7 @@ g.test('constructorMatrix') return [...Array(rows).keys()].map(r => scalar_builder(c * cols + r)); }); - const got = new Matrix(elements); + const got = new MatrixValue(elements); const got_type = got.type; t.expect( got_type.cols === cols, diff --git a/dom/webgpu/tests/cts/checkout/src/unittests/crc32.spec.ts b/dom/webgpu/tests/cts/checkout/src/unittests/crc32.spec.ts new file mode 100644 index 0000000000..5986823c8a --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/unittests/crc32.spec.ts @@ -0,0 +1,28 @@ +export const description = ` +Test for crc32 utility functions. +`; + +import { makeTestGroup } from '../common/framework/test_group.js'; +import { crc32, toHexString } from '../common/util/crc32.js'; + +import { UnitTest } from './unit_test.js'; + +class F extends UnitTest { + test(content: string, expect: string): void { + const got = toHexString(crc32(content)); + this.expect( + expect === got, + ` +expected: ${expect} +got: ${got}` + ); + } +} + +export const g = makeTestGroup(F); + +g.test('strings').fn(t => { + t.test('', '00000000'); + t.test('hello world', '0d4a1185'); + t.test('123456789', 'cbf43926'); +}); diff --git a/dom/webgpu/tests/cts/checkout/src/unittests/floating_point.spec.ts b/dom/webgpu/tests/cts/checkout/src/unittests/floating_point.spec.ts index e8f8525d7f..31501f77ff 100644 --- a/dom/webgpu/tests/cts/checkout/src/unittests/floating_point.spec.ts +++ b/dom/webgpu/tests/cts/checkout/src/unittests/floating_point.spec.ts @@ -5,7 +5,12 @@ Floating Point unit tests. import { makeTestGroup } from '../common/framework/test_group.js'; import { objectEquals, unreachable } from '../common/util/util.js'; import { kValue } from '../webgpu/util/constants.js'; -import { FP, FPInterval, FPIntervalParam, IntervalBounds } from '../webgpu/util/floating_point.js'; +import { + FP, + FPInterval, + FPIntervalParam, + IntervalEndpoints, +} from '../webgpu/util/floating_point.js'; import { map2DArray, oneULPF32, oneULPF16, oneULPF64 } from '../webgpu/util/math.js'; import { reinterpretU16AsF16, @@ -17,24 +22,19 @@ import { UnitTest } from './unit_test.js'; export const g = makeTestGroup(UnitTest); -/** - * For ULP purposes, abstract float behaves like f32, so need to swizzle it in - * for expectations. - */ const kFPTraitForULP = { - abstract: 'f32', f32: 'f32', f16: 'f16', } as const; -/** Bounds indicating an expectation of unbounded error */ -const kUnboundedBounds: IntervalBounds = [Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY]; +/** Endpoints indicating an expectation of unbounded error */ +const kUnboundedEndpoints: IntervalEndpoints = [Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY]; -/** Interval from kUnboundedBounds */ +/** Interval from kUnboundedEndpoints */ const kUnboundedInterval = { - f32: FP.f32.toParam(kUnboundedBounds), - f16: FP.f16.toParam(kUnboundedBounds), - abstract: FP.abstract.toParam(kUnboundedBounds), + f32: FP.f32.toParam(kUnboundedEndpoints), + f16: FP.f16.toParam(kUnboundedEndpoints), + abstract: FP.abstract.toParam(kUnboundedEndpoints), }; /** @returns a number N * ULP greater than the provided number */ @@ -89,17 +89,17 @@ const kMinusOneULPFunctions = { }, }; -/** @returns the expected IntervalBounds adjusted by the given error function +/** @returns the expected IntervalEndpoints adjusted by the given error function * - * @param expected the bounds to be adjusted - * @param error error function to adjust the bounds via + * @param expected the endpoints to be adjusted + * @param error error function to adjust the endpoints via */ function applyError( - expected: number | IntervalBounds, + expected: number | IntervalEndpoints, error: (n: number) => number -): IntervalBounds { +): IntervalEndpoints { // Avoiding going through FPInterval to avoid tying this to a specific kind - const unpack = (n: number | IntervalBounds): [number, number] => { + const unpack = (n: number | IntervalEndpoints): [number, number] => { if (expected instanceof Array) { switch (expected.length) { case 1: @@ -107,7 +107,7 @@ function applyError( case 2: return [expected[0], expected[1]]; } - unreachable(`Tried to unpack an IntervalBounds with length other than 1 or 2`); + unreachable(`Tried to unpack an IntervalEndpoints with length other than 1 or 2`); } else { // TS doesn't narrow this to number automatically return [n as number, n as number]; @@ -128,8 +128,8 @@ function applyError( // FPInterval interface ConstructorCase { - input: IntervalBounds; - expected: IntervalBounds; + input: IntervalEndpoints; + expected: IntervalEndpoints; } g.test('constructor') @@ -160,7 +160,7 @@ g.test('constructor') // Infinities { input: [0, constants.positive.infinity], expected: [0, Number.POSITIVE_INFINITY] }, { input: [constants.negative.infinity, 0], expected: [Number.NEGATIVE_INFINITY, 0] }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, ]; // Note: Out of range values are limited to infinities for abstract float, due to abstract @@ -182,13 +182,13 @@ g.test('constructor') .fn(t => { const i = new FPInterval(t.params.trait, ...t.params.input); t.expect( - objectEquals(i.bounds(), t.params.expected), + objectEquals(i.endpoints(), t.params.expected), `new FPInterval('${t.params.trait}', [${t.params.input}]) returned ${i}. Expected [${t.params.expected}]` ); }); interface ContainsNumberCase { - bounds: number | IntervalBounds; + endpoints: number | IntervalEndpoints; value: number; expected: boolean; } @@ -203,90 +203,90 @@ g.test('contains_number') // prettier-ignore const cases: ContainsNumberCase[] = [ // Common usage - { bounds: [0, 10], value: 0, expected: true }, - { bounds: [0, 10], value: 10, expected: true }, - { bounds: [0, 10], value: 5, expected: true }, - { bounds: [0, 10], value: -5, expected: false }, - { bounds: [0, 10], value: 50, expected: false }, - { bounds: [0, 10], value: Number.NaN, expected: false }, - { bounds: [-5, 10], value: 0, expected: true }, - { bounds: [-5, 10], value: 10, expected: true }, - { bounds: [-5, 10], value: 5, expected: true }, - { bounds: [-5, 10], value: -5, expected: true }, - { bounds: [-5, 10], value: -6, expected: false }, - { bounds: [-5, 10], value: 50, expected: false }, - { bounds: [-5, 10], value: -10, expected: false }, - { bounds: [-1.375, 2.5], value: -10, expected: false }, - { bounds: [-1.375, 2.5], value: 0.5, expected: true }, - { bounds: [-1.375, 2.5], value: 10, expected: false }, + { endpoints: [0, 10], value: 0, expected: true }, + { endpoints: [0, 10], value: 10, expected: true }, + { endpoints: [0, 10], value: 5, expected: true }, + { endpoints: [0, 10], value: -5, expected: false }, + { endpoints: [0, 10], value: 50, expected: false }, + { endpoints: [0, 10], value: Number.NaN, expected: false }, + { endpoints: [-5, 10], value: 0, expected: true }, + { endpoints: [-5, 10], value: 10, expected: true }, + { endpoints: [-5, 10], value: 5, expected: true }, + { endpoints: [-5, 10], value: -5, expected: true }, + { endpoints: [-5, 10], value: -6, expected: false }, + { endpoints: [-5, 10], value: 50, expected: false }, + { endpoints: [-5, 10], value: -10, expected: false }, + { endpoints: [-1.375, 2.5], value: -10, expected: false }, + { endpoints: [-1.375, 2.5], value: 0.5, expected: true }, + { endpoints: [-1.375, 2.5], value: 10, expected: false }, // Point - { bounds: 0, value: 0, expected: true }, - { bounds: 0, value: 10, expected: false }, - { bounds: 0, value: -1000, expected: false }, - { bounds: 10, value: 10, expected: true }, - { bounds: 10, value: 0, expected: false }, - { bounds: 10, value: -10, expected: false }, - { bounds: 10, value: 11, expected: false }, + { endpoints: 0, value: 0, expected: true }, + { endpoints: 0, value: 10, expected: false }, + { endpoints: 0, value: -1000, expected: false }, + { endpoints: 10, value: 10, expected: true }, + { endpoints: 10, value: 0, expected: false }, + { endpoints: 10, value: -10, expected: false }, + { endpoints: 10, value: 11, expected: false }, // Upper infinity - { bounds: [0, constants.positive.infinity], value: constants.positive.min, expected: true }, - { bounds: [0, constants.positive.infinity], value: constants.positive.max, expected: true }, - { bounds: [0, constants.positive.infinity], value: constants.positive.infinity, expected: true }, - { bounds: [0, constants.positive.infinity], value: constants.negative.min, expected: false }, - { bounds: [0, constants.positive.infinity], value: constants.negative.max, expected: false }, - { bounds: [0, constants.positive.infinity], value: constants.negative.infinity, expected: false }, + { endpoints: [0, constants.positive.infinity], value: constants.positive.min, expected: true }, + { endpoints: [0, constants.positive.infinity], value: constants.positive.max, expected: true }, + { endpoints: [0, constants.positive.infinity], value: constants.positive.infinity, expected: true }, + { endpoints: [0, constants.positive.infinity], value: constants.negative.min, expected: false }, + { endpoints: [0, constants.positive.infinity], value: constants.negative.max, expected: false }, + { endpoints: [0, constants.positive.infinity], value: constants.negative.infinity, expected: false }, // Lower infinity - { bounds: [constants.negative.infinity, 0], value: constants.positive.min, expected: false }, - { bounds: [constants.negative.infinity, 0], value: constants.positive.max, expected: false }, - { bounds: [constants.negative.infinity, 0], value: constants.positive.infinity, expected: false }, - { bounds: [constants.negative.infinity, 0], value: constants.negative.min, expected: true }, - { bounds: [constants.negative.infinity, 0], value: constants.negative.max, expected: true }, - { bounds: [constants.negative.infinity, 0], value: constants.negative.infinity, expected: true }, + { endpoints: [constants.negative.infinity, 0], value: constants.positive.min, expected: false }, + { endpoints: [constants.negative.infinity, 0], value: constants.positive.max, expected: false }, + { endpoints: [constants.negative.infinity, 0], value: constants.positive.infinity, expected: false }, + { endpoints: [constants.negative.infinity, 0], value: constants.negative.min, expected: true }, + { endpoints: [constants.negative.infinity, 0], value: constants.negative.max, expected: true }, + { endpoints: [constants.negative.infinity, 0], value: constants.negative.infinity, expected: true }, // Full infinity - { bounds: [constants.negative.infinity, constants.positive.infinity], value: constants.positive.min, expected: true }, - { bounds: [constants.negative.infinity, constants.positive.infinity], value: constants.positive.max, expected: true }, - { bounds: [constants.negative.infinity, constants.positive.infinity], value: constants.positive.infinity, expected: true }, - { bounds: [constants.negative.infinity, constants.positive.infinity], value: constants.negative.min, expected: true }, - { bounds: [constants.negative.infinity, constants.positive.infinity], value: constants.negative.max, expected: true }, - { bounds: [constants.negative.infinity, constants.positive.infinity], value: constants.negative.infinity, expected: true }, - { bounds: [constants.negative.infinity, constants.positive.infinity], value: Number.NaN, expected: true }, + { endpoints: [constants.negative.infinity, constants.positive.infinity], value: constants.positive.min, expected: true }, + { endpoints: [constants.negative.infinity, constants.positive.infinity], value: constants.positive.max, expected: true }, + { endpoints: [constants.negative.infinity, constants.positive.infinity], value: constants.positive.infinity, expected: true }, + { endpoints: [constants.negative.infinity, constants.positive.infinity], value: constants.negative.min, expected: true }, + { endpoints: [constants.negative.infinity, constants.positive.infinity], value: constants.negative.max, expected: true }, + { endpoints: [constants.negative.infinity, constants.positive.infinity], value: constants.negative.infinity, expected: true }, + { endpoints: [constants.negative.infinity, constants.positive.infinity], value: Number.NaN, expected: true }, // Maximum f32 boundary - { bounds: [0, constants.positive.max], value: constants.positive.min, expected: true }, - { bounds: [0, constants.positive.max], value: constants.positive.max, expected: true }, - { bounds: [0, constants.positive.max], value: constants.positive.infinity, expected: false }, - { bounds: [0, constants.positive.max], value: constants.negative.min, expected: false }, - { bounds: [0, constants.positive.max], value: constants.negative.max, expected: false }, - { bounds: [0, constants.positive.max], value: constants.negative.infinity, expected: false }, + { endpoints: [0, constants.positive.max], value: constants.positive.min, expected: true }, + { endpoints: [0, constants.positive.max], value: constants.positive.max, expected: true }, + { endpoints: [0, constants.positive.max], value: constants.positive.infinity, expected: false }, + { endpoints: [0, constants.positive.max], value: constants.negative.min, expected: false }, + { endpoints: [0, constants.positive.max], value: constants.negative.max, expected: false }, + { endpoints: [0, constants.positive.max], value: constants.negative.infinity, expected: false }, // Minimum f32 boundary - { bounds: [constants.negative.min, 0], value: constants.positive.min, expected: false }, - { bounds: [constants.negative.min, 0], value: constants.positive.max, expected: false }, - { bounds: [constants.negative.min, 0], value: constants.positive.infinity, expected: false }, - { bounds: [constants.negative.min, 0], value: constants.negative.min, expected: true }, - { bounds: [constants.negative.min, 0], value: constants.negative.max, expected: true }, - { bounds: [constants.negative.min, 0], value: constants.negative.infinity, expected: false }, + { endpoints: [constants.negative.min, 0], value: constants.positive.min, expected: false }, + { endpoints: [constants.negative.min, 0], value: constants.positive.max, expected: false }, + { endpoints: [constants.negative.min, 0], value: constants.positive.infinity, expected: false }, + { endpoints: [constants.negative.min, 0], value: constants.negative.min, expected: true }, + { endpoints: [constants.negative.min, 0], value: constants.negative.max, expected: true }, + { endpoints: [constants.negative.min, 0], value: constants.negative.infinity, expected: false }, // Subnormals - { bounds: [0, constants.positive.min], value: constants.positive.subnormal.min, expected: true }, - { bounds: [0, constants.positive.min], value: constants.positive.subnormal.max, expected: true }, - { bounds: [0, constants.positive.min], value: constants.negative.subnormal.min, expected: false }, - { bounds: [0, constants.positive.min], value: constants.negative.subnormal.max, expected: false }, - { bounds: [constants.negative.max, 0], value: constants.positive.subnormal.min, expected: false }, - { bounds: [constants.negative.max, 0], value: constants.positive.subnormal.max, expected: false }, - { bounds: [constants.negative.max, 0], value: constants.negative.subnormal.min, expected: true }, - { bounds: [constants.negative.max, 0], value: constants.negative.subnormal.max, expected: true }, - { bounds: [0, constants.positive.subnormal.min], value: constants.positive.subnormal.min, expected: true }, - { bounds: [0, constants.positive.subnormal.min], value: constants.positive.subnormal.max, expected: false }, - { bounds: [0, constants.positive.subnormal.min], value: constants.negative.subnormal.min, expected: false }, - { bounds: [0, constants.positive.subnormal.min], value: constants.negative.subnormal.max, expected: false }, - { bounds: [constants.negative.subnormal.max, 0], value: constants.positive.subnormal.min, expected: false }, - { bounds: [constants.negative.subnormal.max, 0], value: constants.positive.subnormal.max, expected: false }, - { bounds: [constants.negative.subnormal.max, 0], value: constants.negative.subnormal.min, expected: false }, - { bounds: [constants.negative.subnormal.max, 0], value: constants.negative.subnormal.max, expected: true }, + { endpoints: [0, constants.positive.min], value: constants.positive.subnormal.min, expected: true }, + { endpoints: [0, constants.positive.min], value: constants.positive.subnormal.max, expected: true }, + { endpoints: [0, constants.positive.min], value: constants.negative.subnormal.min, expected: false }, + { endpoints: [0, constants.positive.min], value: constants.negative.subnormal.max, expected: false }, + { endpoints: [constants.negative.max, 0], value: constants.positive.subnormal.min, expected: false }, + { endpoints: [constants.negative.max, 0], value: constants.positive.subnormal.max, expected: false }, + { endpoints: [constants.negative.max, 0], value: constants.negative.subnormal.min, expected: true }, + { endpoints: [constants.negative.max, 0], value: constants.negative.subnormal.max, expected: true }, + { endpoints: [0, constants.positive.subnormal.min], value: constants.positive.subnormal.min, expected: true }, + { endpoints: [0, constants.positive.subnormal.min], value: constants.positive.subnormal.max, expected: false }, + { endpoints: [0, constants.positive.subnormal.min], value: constants.negative.subnormal.min, expected: false }, + { endpoints: [0, constants.positive.subnormal.min], value: constants.negative.subnormal.max, expected: false }, + { endpoints: [constants.negative.subnormal.max, 0], value: constants.positive.subnormal.min, expected: false }, + { endpoints: [constants.negative.subnormal.max, 0], value: constants.positive.subnormal.max, expected: false }, + { endpoints: [constants.negative.subnormal.max, 0], value: constants.negative.subnormal.min, expected: false }, + { endpoints: [constants.negative.subnormal.max, 0], value: constants.negative.subnormal.max, expected: true }, ]; // Note: Out of range values are limited to infinities for abstract float, due to abstract @@ -296,20 +296,20 @@ g.test('contains_number') // prettier-ignore cases.push(...[ // Out of range high - { bounds: [0, 2 * constants.positive.max], value: constants.positive.min, expected: true }, - { bounds: [0, 2 * constants.positive.max], value: constants.positive.max, expected: true }, - { bounds: [0, 2 * constants.positive.max], value: constants.positive.infinity, expected: false }, - { bounds: [0, 2 * constants.positive.max], value: constants.negative.min, expected: false }, - { bounds: [0, 2 * constants.positive.max], value: constants.negative.max, expected: false }, - { bounds: [0, 2 * constants.positive.max], value: constants.negative.infinity, expected: false }, + { endpoints: [0, 2 * constants.positive.max], value: constants.positive.min, expected: true }, + { endpoints: [0, 2 * constants.positive.max], value: constants.positive.max, expected: true }, + { endpoints: [0, 2 * constants.positive.max], value: constants.positive.infinity, expected: false }, + { endpoints: [0, 2 * constants.positive.max], value: constants.negative.min, expected: false }, + { endpoints: [0, 2 * constants.positive.max], value: constants.negative.max, expected: false }, + { endpoints: [0, 2 * constants.positive.max], value: constants.negative.infinity, expected: false }, // Out of range low - { bounds: [2 * constants.negative.min, 0], value: constants.positive.min, expected: false }, - { bounds: [2 * constants.negative.min, 0], value: constants.positive.max, expected: false }, - { bounds: [2 * constants.negative.min, 0], value: constants.positive.infinity, expected: false }, - { bounds: [2 * constants.negative.min, 0], value: constants.negative.min, expected: true }, - { bounds: [2 * constants.negative.min, 0], value: constants.negative.max, expected: true }, - { bounds: [2 * constants.negative.min, 0], value: constants.negative.infinity, expected: false }, + { endpoints: [2 * constants.negative.min, 0], value: constants.positive.min, expected: false }, + { endpoints: [2 * constants.negative.min, 0], value: constants.positive.max, expected: false }, + { endpoints: [2 * constants.negative.min, 0], value: constants.positive.infinity, expected: false }, + { endpoints: [2 * constants.negative.min, 0], value: constants.negative.min, expected: true }, + { endpoints: [2 * constants.negative.min, 0], value: constants.negative.max, expected: true }, + { endpoints: [2 * constants.negative.min, 0], value: constants.negative.infinity, expected: false }, ] as ContainsNumberCase[]); } @@ -318,7 +318,7 @@ g.test('contains_number') ) .fn(t => { const trait = FP[t.params.trait]; - const i = trait.toInterval(t.params.bounds); + const i = trait.toInterval(t.params.endpoints); const value = t.params.value; const expected = t.params.expected; @@ -327,8 +327,8 @@ g.test('contains_number') }); interface ContainsIntervalCase { - lhs: number | IntervalBounds; - rhs: number | IntervalBounds; + lhs: number | IntervalEndpoints; + rhs: number | IntervalEndpoints; expected: boolean; } @@ -440,8 +440,8 @@ g.test('contains_interval') // Utilities interface SpanIntervalsCase { - intervals: (number | IntervalBounds)[]; - expected: number | IntervalBounds; + intervals: (number | IntervalEndpoints)[]; + expected: number | IntervalEndpoints; } g.test('spanIntervals') @@ -467,7 +467,7 @@ g.test('spanIntervals') { intervals: [[2, 5], [0, 1]], expected: [0, 5] }, { intervals: [[0, 2], [1, 5]], expected: [0, 5] }, { intervals: [[0, 5], [1, 2]], expected: [0, 5] }, - { intervals: [[constants.negative.infinity, 0], [0, constants.positive.infinity]], expected: kUnboundedBounds }, + { intervals: [[constants.negative.infinity, 0], [0, constants.positive.infinity]], expected: kUnboundedEndpoints }, // Multiple Intervals { intervals: [[0, 1], [2, 3], [4, 5]], expected: [0, 5] }, @@ -494,7 +494,7 @@ g.test('spanIntervals') }); interface isVectorCase { - input: (number | IntervalBounds | FPIntervalParam)[]; + input: (number | IntervalEndpoints | FPIntervalParam)[]; expected: boolean; } @@ -511,7 +511,7 @@ g.test('isVector') { input: [1, 2, 3], expected: false }, { input: [1, 2, 3, 4], expected: false }, - // IntervalBounds + // IntervalEndpoints { input: [[1], [2]], expected: false }, { input: [[1], [2], [3]], expected: false }, { input: [[1], [2], [3], [4]], expected: false }, @@ -600,8 +600,8 @@ g.test('isVector') }); interface toVectorCase { - input: (number | IntervalBounds | FPIntervalParam)[]; - expected: (number | IntervalBounds)[]; + input: (number | IntervalEndpoints | FPIntervalParam)[]; + expected: (number | IntervalEndpoints)[]; } g.test('toVector') @@ -617,7 +617,7 @@ g.test('toVector') { input: [1, 2, 3], expected: [1, 2, 3] }, { input: [1, 2, 3, 4], expected: [1, 2, 3, 4] }, - // IntervalBounds + // IntervalEndpoints { input: [[1], [2]], expected: [1, 2] }, { input: [[1], [2], [3]], expected: [1, 2, 3] }, { input: [[1], [2], [3], [4]], expected: [1, 2, 3, 4] }, @@ -704,7 +704,7 @@ g.test('toVector') { input: [1, trait.toParam([2]), [3], 4], expected: [1, 2, 3, 4] }, { input: [1, [2], [2, 3], kUnboundedInterval[p.trait]], - expected: [1, 2, [2, 3], kUnboundedBounds], + expected: [1, 2, [2, 3], kUnboundedEndpoints], }, ]; }) @@ -722,7 +722,7 @@ g.test('toVector') }); interface isMatrixCase { - input: (number | IntervalBounds | FPIntervalParam)[][]; + input: (number | IntervalEndpoints | FPIntervalParam)[][]; expected: boolean; } @@ -808,7 +808,7 @@ g.test('isMatrix') expected: false, }, - // IntervalBounds + // IntervalEndpoints { input: [ [[1], [2]], @@ -1157,8 +1157,8 @@ g.test('isMatrix') }); interface toMatrixCase { - input: (number | IntervalBounds | FPIntervalParam)[][]; - expected: (number | IntervalBounds)[][]; + input: (number | IntervalEndpoints | FPIntervalParam)[][]; + expected: (number | IntervalEndpoints)[][]; } g.test('toMatrix') @@ -1279,7 +1279,7 @@ g.test('toMatrix') ], }, - // IntervalBounds + // IntervalEndpoints { input: [ [[1], [2]], @@ -1822,7 +1822,7 @@ g.test('toMatrix') interface AbsoluteErrorCase { value: number; error: number; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } // Special values used for testing absolute error interval @@ -1856,23 +1856,24 @@ g.test('absoluteErrorInterval') const smallErr = kSmallAbsoluteErrorValue[p.trait]; const largeErr = kLargeAbsoluteErrorValue[p.trait]; const subnormalErr = kSubnormalAbsoluteErrorValue[p.trait]; + // prettier-ignore return [ // Edge Cases - // 1. Interval around infinity would be kUnboundedBounds - { value: constants.positive.infinity, error: 0, expected: kUnboundedBounds }, - { value: constants.positive.infinity, error: largeErr, expected: kUnboundedBounds }, - { value: constants.positive.infinity, error: 1, expected: kUnboundedBounds }, - { value: constants.negative.infinity, error: 0, expected: kUnboundedBounds }, - { value: constants.negative.infinity, error: largeErr, expected: kUnboundedBounds }, - { value: constants.negative.infinity, error: 1, expected: kUnboundedBounds }, + // 1. Interval around infinity would be kUnboundedEndpoints + { value: constants.positive.infinity, error: 0, expected: kUnboundedEndpoints }, + { value: constants.positive.infinity, error: largeErr, expected: kUnboundedEndpoints }, + { value: constants.positive.infinity, error: 1, expected: kUnboundedEndpoints }, + { value: constants.negative.infinity, error: 0, expected: kUnboundedEndpoints }, + { value: constants.negative.infinity, error: largeErr, expected: kUnboundedEndpoints }, + { value: constants.negative.infinity, error: 1, expected: kUnboundedEndpoints }, // 2. Interval around largest finite positive/negative { value: constants.positive.max, error: 0, expected: constants.positive.max }, - { value: constants.positive.max, error: largeErr, expected: kUnboundedBounds}, - { value: constants.positive.max, error: constants.positive.max, expected: kUnboundedBounds}, + { value: constants.positive.max, error: largeErr, expected: kUnboundedEndpoints}, + { value: constants.positive.max, error: constants.positive.max, expected: kUnboundedEndpoints}, { value: constants.negative.min, error: 0, expected: constants.negative.min }, - { value: constants.negative.min, error: largeErr, expected: kUnboundedBounds}, - { value: constants.negative.min, error: constants.positive.max, expected: kUnboundedBounds}, + { value: constants.negative.min, error: largeErr, expected: kUnboundedEndpoints}, + { value: constants.negative.min, error: constants.positive.max, expected: kUnboundedEndpoints}, // 3. Interval around small but normal center, center should not get flushed. { value: constants.positive.min, error: 0, expected: constants.positive.min }, { value: constants.positive.min, error: smallErr, expected: [constants.positive.min - smallErr, constants.positive.min + smallErr]}, @@ -1898,6 +1899,19 @@ g.test('absoluteErrorInterval') { value: constants.negative.subnormal.max, error: smallErr, expected: [constants.negative.subnormal.max - smallErr, smallErr] }, { value: constants.negative.subnormal.max, error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, + // Zero + { value: 0, error: 0, expected: 0 }, + { value: 0, error: smallErr, expected: [-smallErr, smallErr] }, + { value: 0, error: 1, expected: [-1, 1] }, + + // Two + { value: 2, error: 0, expected: 2 }, + { value: 2, error: smallErr, expected: [2 - smallErr, 2 + smallErr] }, + { value: 2, error: 1, expected: [1, 3] }, + { value: -2, error: 0, expected: -2 }, + { value: -2, error: smallErr, expected: [-2 - smallErr, -2 + smallErr] }, + { value: -2, error: 1, expected: [-3, -1] }, + // 64-bit subnormals, expected to be treated as 0.0 or smallest subnormal of kind. { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: 0, expected: [0, constants.positive.subnormal.min] }, { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), error: subnormalErr, expected: [-subnormalErr, constants.positive.subnormal.min + subnormalErr] }, @@ -1912,19 +1926,6 @@ g.test('absoluteErrorInterval') { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 0, expected: [constants.negative.subnormal.max, 0] }, { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: subnormalErr, expected: [constants.negative.subnormal.max - subnormalErr, subnormalErr] }, { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), error: 1, expected: [constants.negative.subnormal.max - 1, 1] }, - - // Zero - { value: 0, error: 0, expected: 0 }, - { value: 0, error: smallErr, expected: [-smallErr, smallErr] }, - { value: 0, error: 1, expected: [-1, 1] }, - - // Two - { value: 2, error: 0, expected: 2 }, - { value: 2, error: smallErr, expected: [2 - smallErr, 2 + smallErr] }, - { value: 2, error: 1, expected: [1, 3] }, - { value: -2, error: 0, expected: -2 }, - { value: -2, error: smallErr, expected: [-2 - smallErr, -2 + smallErr] }, - { value: -2, error: 1, expected: [-3, -1] }, ]; }) ) @@ -1942,7 +1943,7 @@ g.test('absoluteErrorInterval') interface CorrectlyRoundedCase { value: number; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } // Correctly rounded cases that input values are exactly representable normal values of target type @@ -2034,8 +2035,8 @@ g.test('correctlyRoundedInterval') // prettier-ignore return [ // Edge Cases - { value: constants.positive.infinity, expected: kUnboundedBounds }, - { value: constants.negative.infinity, expected: kUnboundedBounds }, + { value: constants.positive.infinity, expected: kUnboundedEndpoints }, + { value: constants.negative.infinity, expected: kUnboundedEndpoints }, { value: constants.positive.max, expected: constants.positive.max }, { value: constants.negative.min, expected: constants.negative.min }, { value: constants.positive.min, expected: constants.positive.min }, @@ -2074,7 +2075,7 @@ g.test('correctlyRoundedInterval') interface ULPCase { value: number; num_ulp: number; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } // Special values used for testing ULP error interval @@ -2086,7 +2087,7 @@ const kULPErrorValue = { g.test('ulpInterval') .params(u => u - .combine('trait', ['abstract', 'f32', 'f16'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams<ULPCase>(p => { const trait = kFPTraitForULP[p.trait]; @@ -2099,21 +2100,21 @@ g.test('ulpInterval') // prettier-ignore return [ // Edge Cases - { value: constants.positive.infinity, num_ulp: 0, expected: kUnboundedBounds }, - { value: constants.positive.infinity, num_ulp: 1, expected: kUnboundedBounds }, - { value: constants.positive.infinity, num_ulp: ULPValue, expected: kUnboundedBounds }, - { value: constants.negative.infinity, num_ulp: 0, expected: kUnboundedBounds }, - { value: constants.negative.infinity, num_ulp: 1, expected: kUnboundedBounds }, - { value: constants.negative.infinity, num_ulp: ULPValue, expected: kUnboundedBounds }, + { value: constants.positive.infinity, num_ulp: 0, expected: kUnboundedEndpoints }, + { value: constants.positive.infinity, num_ulp: 1, expected: kUnboundedEndpoints }, + { value: constants.positive.infinity, num_ulp: ULPValue, expected: kUnboundedEndpoints }, + { value: constants.negative.infinity, num_ulp: 0, expected: kUnboundedEndpoints }, + { value: constants.negative.infinity, num_ulp: 1, expected: kUnboundedEndpoints }, + { value: constants.negative.infinity, num_ulp: ULPValue, expected: kUnboundedEndpoints }, { value: constants.positive.max, num_ulp: 0, expected: constants.positive.max }, - { value: constants.positive.max, num_ulp: 1, expected: kUnboundedBounds }, - { value: constants.positive.max, num_ulp: ULPValue, expected: kUnboundedBounds }, + { value: constants.positive.max, num_ulp: 1, expected: kUnboundedEndpoints }, + { value: constants.positive.max, num_ulp: ULPValue, expected: kUnboundedEndpoints }, { value: constants.positive.min, num_ulp: 0, expected: constants.positive.min }, { value: constants.positive.min, num_ulp: 1, expected: [0, plusOneULP(constants.positive.min)] }, { value: constants.positive.min, num_ulp: ULPValue, expected: [0, plusNULP(constants.positive.min, ULPValue)] }, { value: constants.negative.min, num_ulp: 0, expected: constants.negative.min }, - { value: constants.negative.min, num_ulp: 1, expected: kUnboundedBounds }, - { value: constants.negative.min, num_ulp: ULPValue, expected: kUnboundedBounds }, + { value: constants.negative.min, num_ulp: 1, expected: kUnboundedEndpoints }, + { value: constants.negative.min, num_ulp: ULPValue, expected: kUnboundedEndpoints }, { value: constants.negative.max, num_ulp: 0, expected: constants.negative.max }, { value: constants.negative.max, num_ulp: 1, expected: [minusOneULP(constants.negative.max), 0] }, { value: constants.negative.max, num_ulp: ULPValue, expected: [minusNULP(constants.negative.max, ULPValue), 0] }, @@ -2178,7 +2179,7 @@ const kConstantCorrectlyRoundedExpectation = { '1.9': [reinterpretU32AsF32(0x3ff33333), reinterpretU32AsF32(0x3ff33334)], // -1.9 falls between f32 0xBFF33334 and 0xBFF33333 '-1.9': [reinterpretU32AsF32(0xbff33334), reinterpretU32AsF32(0xbff33333)], - } as { [value in ConstantNumberFrequentlyUsedInCases]: IntervalBounds }, + } as { [value in ConstantNumberFrequentlyUsedInCases]: IntervalEndpoints }, f16: { // 0.1 falls between f16 0x2E66 and 0x2E67 '0.1': [reinterpretU16AsF16(0x2e66), reinterpretU16AsF16(0x2e67)], @@ -2188,7 +2189,7 @@ const kConstantCorrectlyRoundedExpectation = { '1.9': [reinterpretU16AsF16(0x3f99), reinterpretU16AsF16(0x3f9a)], // 1.9 falls between f16 0xBF9A and 0xBF99 '-1.9': [reinterpretU16AsF16(0xbf9a), reinterpretU16AsF16(0xbf99)], - } as { [value in ConstantNumberFrequentlyUsedInCases]: IntervalBounds }, + } as { [value in ConstantNumberFrequentlyUsedInCases]: IntervalEndpoints }, // Since abstract is actually f64 and JS number is also f64, the JS number value will map to // identical abstracty value without rounded. abstract: { @@ -2201,7 +2202,7 @@ const kConstantCorrectlyRoundedExpectation = { interface ScalarToIntervalCase { input: number; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('absInterval') @@ -2224,8 +2225,8 @@ g.test('absInterval') { input: -1.9, expected: kConstantCorrectlyRoundedExpectation[p.trait]['1.9']}, // Edge cases - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: constants.positive.max, expected: constants.positive.max }, { input: constants.positive.min, expected: constants.positive.min }, { input: constants.negative.min, expected: constants.positive.max }, @@ -2290,17 +2291,18 @@ g.test('acosInterval') const constants = trait.constants(); // prettier-ignore return [ - // The acceptance interval @ x = -1 and 1 is kUnboundedBounds, because - // sqrt(1 - x*x) = sqrt(0), and sqrt is defined in terms of inverseqrt - // The acceptance interval @ x = 0 is kUnboundedBounds, because atan2 is not - // well-defined/implemented at 0. - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: -1, expected: kUnboundedBounds }, - { input: 0, expected: kUnboundedBounds }, - { input: 1, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + // The acceptance interval @ x = -1 and 1 is kUnboundedEndpoints, + // because sqrt(1 - x*x) = sqrt(0), and sqrt is defined in terms of + // inverseqrt. + // The acceptance interval @ x = 0 is kUnboundedEndpoints, because atan2 + // is not well-defined/implemented at 0. + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: -1, expected: kUnboundedEndpoints }, + { input: 0, expected: kUnboundedEndpoints }, + { input: 1, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, // Cases that bounded by absolute error and inherited from atan2(sqrt(1-x*x), x). Note that // even x is very close to 1.0 and the expected result is close to 0.0, the expected @@ -2346,13 +2348,13 @@ g.test('acoshAlternativeInterval') return [ ...kAcoshAlternativeIntervalCases[p.trait], - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: -1, expected: kUnboundedBounds }, - { input: 0, expected: kUnboundedBounds }, - { input: 1, expected: kUnboundedBounds }, // 1/0 occurs in inverseSqrt in this formulation - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: -1, expected: kUnboundedEndpoints }, + { input: 0, expected: kUnboundedEndpoints }, + { input: 1, expected: kUnboundedEndpoints }, // 1/0 occurs in inverseSqrt in this formulation + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -2392,13 +2394,13 @@ g.test('acoshPrimaryInterval') return [ ...kAcoshPrimaryIntervalCases[p.trait], - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: -1, expected: kUnboundedBounds }, - { input: 0, expected: kUnboundedBounds }, - { input: 1, expected: kUnboundedBounds }, // 1/0 occurs in inverseSqrt in this formulation - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: -1, expected: kUnboundedEndpoints }, + { input: 0, expected: kUnboundedEndpoints }, + { input: 1, expected: kUnboundedEndpoints }, // 1/0 occurs in inverseSqrt in this formulation + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -2434,28 +2436,29 @@ g.test('asinInterval') .expandWithParams<ScalarToIntervalCase>(p => { const trait = FP[p.trait]; const constants = trait.constants(); - const abs_error = p.trait === 'f32' ? 6.77e-5 : 3.91e-3; + const abs_error = p.trait === 'f32' ? 6.81e-5 : 3.91e-3; // prettier-ignore return [ - // The acceptance interval @ x = -1 and 1 is kUnboundedBounds, because - // sqrt(1 - x*x) = sqrt(0), and sqrt is defined in terms of inversqrt. - // The acceptance interval @ x = 0 is kUnboundedBounds, because atan2 is not - // well-defined/implemented at 0. - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: -1, expected: kUnboundedBounds }, - // Subnormal input may get flushed to 0, and result in kUnboundedBounds. - { input: constants.negative.subnormal.min, expected: kUnboundedBounds }, - { input: 0, expected: kUnboundedBounds }, - { input: constants.positive.subnormal.max, expected: kUnboundedBounds }, - { input: 1, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + // The acceptance interval @ x = -1 and 1 is kUnboundedEndpoints, + // because sqrt(1 - x*x) = sqrt(0), and sqrt is defined in terms of + // inversqrt. + // The acceptance interval @ x = 0 is kUnboundedEndpoints, because + // atan2 is not well-defined/implemented at 0. + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: -1, expected: kUnboundedEndpoints }, + // Subnormal input may get flushed to 0, and result in kUnboundedEndpoints. + { input: constants.negative.subnormal.min, expected: kUnboundedEndpoints }, + { input: 0, expected: kUnboundedEndpoints }, + { input: constants.positive.subnormal.max, expected: kUnboundedEndpoints }, + { input: 1, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, // When input near 0, the expected result is bounded by absolute error rather than ULP // error. Away from 0 the atan2 inherited error should be larger. - { input: constants.negative.max, expected: trait.absoluteErrorInterval(Math.asin(constants.negative.max), abs_error).bounds() }, // ~0 - { input: constants.positive.min, expected: trait.absoluteErrorInterval(Math.asin(constants.positive.min), abs_error).bounds() }, // ~0 + { input: constants.negative.max, expected: trait.absoluteErrorInterval(Math.asin(constants.negative.max), abs_error).endpoints() }, // ~0 + { input: constants.positive.min, expected: trait.absoluteErrorInterval(Math.asin(constants.positive.min), abs_error).endpoints() }, // ~0 // Cases that inherited from atan2(x, sqrt(1-x*x)) ...kAsinIntervalInheritedCases[p.trait], @@ -2500,10 +2503,10 @@ g.test('asinhInterval') return [ ...kAsinhIntervalCases[p.trait], - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -2569,8 +2572,8 @@ g.test('atanInterval') { input: 0, expected: 0 }, ...kAtanIntervalCases[p.trait], - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -2619,12 +2622,12 @@ g.test('atanhInterval') return [ ...kAtanhIntervalCases[p.trait], - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: -1, expected: kUnboundedBounds }, - { input: 1, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: -1, expected: kUnboundedEndpoints }, + { input: 1, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -2650,12 +2653,17 @@ const kCeilIntervalCases = { { input: -(2 ** 14), expected: -(2 ** 14) }, { input: 0x8000, expected: 0x8000 }, // https://github.com/gpuweb/cts/issues/2766 ], + abstract: [ + { input: 2 ** 52, expected: 2 ** 52 }, + { input: -(2 ** 52), expected: -(2 ** 52) }, + { input: 0x8000000000000000, expected: 0x8000000000000000 }, // https://github.com/gpuweb/cts/issues/2766 + ], } as const; g.test('ceilInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams<ScalarToIntervalCase>(p => { const constants = FP[p.trait].constants(); @@ -2674,15 +2682,15 @@ g.test('ceilInterval') { input: -1.9, expected: -1 }, // Edge cases - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: constants.positive.max, expected: constants.positive.max }, { input: constants.positive.min, expected: 1 }, { input: constants.negative.min, expected: constants.negative.min }, { input: constants.negative.max, expected: 0 }, ...kCeilIntervalCases[p.trait], - // 32-bit subnormals + // Subnormals { input: constants.positive.subnormal.max, expected: [0, 1] }, { input: constants.positive.subnormal.min, expected: [0, 1] }, { input: constants.negative.subnormal.min, expected: 0 }, @@ -2714,12 +2722,12 @@ const kCosIntervalThirdPiCases = { // cos(-1.046875) = 0.50027931 { input: kValue.f16.negative.pi.third, - expected: FP['f16'].correctlyRoundedInterval(0.50027931).bounds(), + expected: FP['f16'].correctlyRoundedInterval(0.50027931).endpoints(), }, // cos(1.046875) = 0.50027931 { input: kValue.f16.positive.pi.third, - expected: FP['f16'].correctlyRoundedInterval(0.50027931).bounds(), + expected: FP['f16'].correctlyRoundedInterval(0.50027931).endpoints(), }, ], }; @@ -2740,13 +2748,13 @@ g.test('cosInterval') // substantially different, so instead of getting 0 you get a value on the // order of 10^-8 away from 0, thus difficult to express in a // human-readable manner. - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, { input: constants.negative.pi.whole, expected: [-1, kPlusOneULPFunctions[p.trait](-1)] }, { input: 0, expected: [1, 1] }, { input: constants.positive.pi.whole, expected: [-1, kPlusOneULPFunctions[p.trait](-1)] }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ...(kCosIntervalThirdPiCases[p.trait] as ScalarToIntervalCase[]), ]; @@ -2796,10 +2804,10 @@ g.test('coshInterval') return [ ...kCoshIntervalCases[p.trait], - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -2843,37 +2851,23 @@ const kDegreesIntervalCases = { { input: kValue.f16.positive.pi.three_quarters, expected: [kMinusOneULPFunctions['f16'](135), 135] }, { input: kValue.f16.positive.pi.whole, expected: [kMinusOneULPFunctions['f16'](180), 180] }, ] as ScalarToIntervalCase[], - abstract: [ - { input: kValue.f64.negative.pi.whole, expected: -180 }, - { input: kValue.f64.negative.pi.three_quarters, expected: -135 }, - { input: kValue.f64.negative.pi.half, expected: -90 }, - { input: kValue.f64.negative.pi.third, expected: kPlusOneULPFunctions['abstract'](-60) }, - { input: kValue.f64.negative.pi.quarter, expected: -45 }, - { input: kValue.f64.negative.pi.sixth, expected: kPlusOneULPFunctions['abstract'](-30) }, - { input: kValue.f64.positive.pi.sixth, expected: kMinusOneULPFunctions['abstract'](30) }, - { input: kValue.f64.positive.pi.quarter, expected: 45 }, - { input: kValue.f64.positive.pi.third, expected: kMinusOneULPFunctions['abstract'](60) }, - { input: kValue.f64.positive.pi.half, expected: 90 }, - { input: kValue.f64.positive.pi.three_quarters, expected: 135 }, - { input: kValue.f64.positive.pi.whole, expected: 180 }, - ] as ScalarToIntervalCase[], } as const; g.test('degreesInterval') .params(u => u - .combine('trait', ['f32', 'f16', 'abstract'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams<ScalarToIntervalCase>(p => { const trait = p.trait; const constants = FP[trait].constants(); // prettier-ignore return [ - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, { input: 0, expected: 0 }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, ...kDegreesIntervalCases[trait] ]; }) @@ -2895,14 +2889,14 @@ const kExpIntervalCases = { // exp(88) = 1.6516362549940018555283297962649e+38 = 0x7ef882b6/7. { input: 88, expected: [reinterpretU32AsF32(0x7ef882b6), reinterpretU32AsF32(0x7ef882b7)] }, // exp(89) overflow f32. - { input: 89, expected: kUnboundedBounds }, + { input: 89, expected: kUnboundedEndpoints }, ] as ScalarToIntervalCase[], f16: [ { input: 1, expected: [kValue.f16.positive.e, kPlusOneULPFunctions['f16'](kValue.f16.positive.e)] }, // exp(11) = 59874.141715197818455326485792258 = 0x7b4f/0x7b50. { input: 11, expected: [reinterpretU16AsF16(0x7b4f), reinterpretU16AsF16(0x7b50)] }, // exp(12) = 162754.79141900392080800520489849 overflow f16. - { input: 12, expected: kUnboundedBounds }, + { input: 12, expected: kUnboundedEndpoints }, ] as ScalarToIntervalCase[], } as const; @@ -2916,7 +2910,7 @@ g.test('expInterval') const constants = FP[trait].constants(); // prettier-ignore return [ - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: 0, expected: 1 }, ...kExpIntervalCases[trait], ]; @@ -2954,13 +2948,13 @@ const kExp2IntervalCases = { // exp2(127) = 1.7014118346046923173168730371588e+38 = 0x7f000000, 3 + 2 * 127 = 258 ulps. { input: 127, expected: reinterpretU32AsF32(0x7f000000) }, // exp2(128) overflow f32. - { input: 128, expected: kUnboundedBounds }, + { input: 128, expected: kUnboundedEndpoints }, ] as ScalarToIntervalCase[], f16: [ // exp2(15) = 32768 = 0x7800, 1 + 2 * 15 = 31 ulps { input: 15, expected: reinterpretU16AsF16(0x7800) }, // exp2(16) = 65536 overflow f16. - { input: 16, expected: kUnboundedBounds }, + { input: 16, expected: kUnboundedEndpoints }, ] as ScalarToIntervalCase[], } as const; @@ -2974,7 +2968,7 @@ g.test('exp2Interval') const constants = FP[trait].constants(); // prettier-ignore return [ - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: 0, expected: 1 }, { input: 1, expected: 2 }, ...kExp2IntervalCases[trait], @@ -3051,8 +3045,8 @@ g.test('floorInterval') { input: -1.9, expected: -2 }, // Edge cases - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: constants.positive.max, expected: constants.positive.max }, { input: constants.positive.min, expected: 0 }, { input: constants.negative.min, expected: constants.negative.min }, @@ -3099,12 +3093,24 @@ const kFractIntervalCases = { { input: -1.1, expected: [reinterpretU16AsF16(0x3b32), reinterpretU16AsF16(0x3b34)] }, // ~0.9 { input: 658.5, expected: 0.5 }, ] as ScalarToIntervalCase[], + abstract: [ + { input: 0.1, expected: reinterpretU64AsF64(0x3fb999999999999an) }, + { input: 0.9, expected: reinterpretU64AsF64(0x3feccccccccccccdn) }, + { input: 1.1, expected: reinterpretU64AsF64(0x3fb99999999999a0n) }, + { input: -0.1, expected: reinterpretU64AsF64(0x3feccccccccccccdn) }, + { input: -0.9, expected: reinterpretU64AsF64(0x3fb9999999999998n) }, + { input: -1.1, expected: reinterpretU64AsF64(0x3fecccccccccccccn) }, + + // https://github.com/gpuweb/cts/issues/2766 + { input: 0x80000000, expected: 0 }, + ] as ScalarToIntervalCase[], + } as const; g.test('fractInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams<ScalarToIntervalCase>(p => { const constants = FP[p.trait].constants(); @@ -3117,8 +3123,8 @@ g.test('fractInterval') ...kFractIntervalCases[p.trait], // Edge cases - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: constants.positive.max, expected: 0 }, { input: constants.positive.min, expected: constants.positive.min }, { input: constants.negative.min, expected: 0 }, @@ -3180,9 +3186,9 @@ g.test('inverseSqrtInterval') { input: 100, expected: kConstantCorrectlyRoundedExpectation[p.trait]['0.1'] }, // ~0.1 // Out of definition domain - { input: -1, expected: kUnboundedBounds }, - { input: 0, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: -1, expected: kUnboundedEndpoints }, + { input: 0, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -3215,7 +3221,7 @@ const kRootSumSquareExpectionInterval = { '[1.0, 1.0]' : [reinterpretU64AsF64(0x3ff6_a09d_b000_0000n), reinterpretU64AsF64(0x3ff6_a09f_1000_0000n)], // ~√2 '[1.0, 1.0, 1.0]' : [reinterpretU64AsF64(0x3ffb_b67a_1000_0000n), reinterpretU64AsF64(0x3ffb_b67b_b000_0000n)], // ~√3 '[1.0, 1.0, 1.0, 1.0]' : [reinterpretU64AsF64(0x3fff_ffff_7000_0000n), reinterpretU64AsF64(0x4000_0000_9000_0000n)], // ~2 - } as {[s: string]: IntervalBounds}, + } as {[s: string]: IntervalEndpoints}, f16: { '[0.1]': [reinterpretU64AsF64(0x3fb9_7e00_0000_0000n), reinterpretU64AsF64(0x3fb9_b600_0000_0000n)], // ~0.1 '[1.0]' : [reinterpretU64AsF64(0x3fef_ee00_0000_0000n), reinterpretU64AsF64(0x3ff0_1200_0000_0000n)], // ~1.0 @@ -3223,7 +3229,7 @@ const kRootSumSquareExpectionInterval = { '[1.0, 1.0]' : [reinterpretU64AsF64(0x3ff6_8a00_0000_0000n), reinterpretU64AsF64(0x3ff6_b600_0000_0000n)], // ~√2 '[1.0, 1.0, 1.0]' : [reinterpretU64AsF64(0x3ffb_9a00_0000_0000n), reinterpretU64AsF64(0x3ffb_d200_0000_0000n)], // ~√3 '[1.0, 1.0, 1.0, 1.0]' : [reinterpretU64AsF64(0x3fff_ee00_0000_0000n), reinterpretU64AsF64(0x4000_1200_0000_0000n)], // ~2 - } as {[s: string]: IntervalBounds}, + } as {[s: string]: IntervalEndpoints}, } as const; g.test('lengthIntervalScalar') @@ -3243,22 +3249,22 @@ g.test('lengthIntervalScalar') {input: 10.0, expected: kRootSumSquareExpectionInterval[p.trait]['[10]'] }, // ~10 {input: -10.0, expected: kRootSumSquareExpectionInterval[p.trait]['[10]'] }, // ~10 - // length(0) = kUnboundedBounds, because length uses sqrt, which is defined as 1/inversesqrt - {input: 0, expected: kUnboundedBounds }, + // length(0) = kUnboundedEndpoints, because length uses sqrt, which is defined as 1/inversesqrt + {input: 0, expected: kUnboundedEndpoints }, // Subnormal Cases - { input: constants.negative.subnormal.min, expected: kUnboundedBounds }, - { input: constants.negative.subnormal.max, expected: kUnboundedBounds }, - { input: constants.positive.subnormal.min, expected: kUnboundedBounds }, - { input: constants.positive.subnormal.max, expected: kUnboundedBounds }, + { input: constants.negative.subnormal.min, expected: kUnboundedEndpoints }, + { input: constants.negative.subnormal.max, expected: kUnboundedEndpoints }, + { input: constants.positive.subnormal.min, expected: kUnboundedEndpoints }, + { input: constants.positive.subnormal.max, expected: kUnboundedEndpoints }, // Edge cases - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: constants.negative.max, expected: kUnboundedBounds }, - { input: constants.positive.min, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: constants.negative.max, expected: kUnboundedEndpoints }, + { input: constants.positive.min, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, ]; }) ) @@ -3300,8 +3306,8 @@ g.test('logInterval') .expandWithParams<ScalarToIntervalCase>(p => { // prettier-ignore return [ - { input: -1, expected: kUnboundedBounds }, - { input: 0, expected: kUnboundedBounds }, + { input: -1, expected: kUnboundedEndpoints }, + { input: 0, expected: kUnboundedEndpoints }, { input: 1, expected: 0 }, ...kLogIntervalCases[p.trait], ]; @@ -3348,8 +3354,8 @@ g.test('log2Interval') .expandWithParams<ScalarToIntervalCase>(p => { // prettier-ignore return [ - { input: -1, expected: kUnboundedBounds }, - { input: 0, expected: kUnboundedBounds }, + { input: -1, expected: kUnboundedEndpoints }, + { input: 0, expected: kUnboundedEndpoints }, { input: 1, expected: 0 }, { input: 2, expected: 1 }, { input: 16, expected: 4 }, @@ -3387,8 +3393,8 @@ g.test('negationInterval') // prettier-ignore return [ // Edge cases - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: constants.positive.max, expected: constants.negative.min }, { input: constants.positive.min, expected: constants.negative.max }, { input: constants.negative.min, expected: constants.positive.max }, @@ -3425,8 +3431,8 @@ g.test('quantizeToF16Interval') .paramsSubcasesOnly<ScalarToIntervalCase>( // prettier-ignore [ - { input: kValue.f32.negative.infinity, expected: kUnboundedBounds }, - { input: kValue.f32.negative.min, expected: kUnboundedBounds }, + { input: kValue.f32.negative.infinity, expected: kUnboundedEndpoints }, + { input: kValue.f32.negative.min, expected: kUnboundedEndpoints }, { input: kValue.f16.negative.min, expected: kValue.f16.negative.min }, { input: -1.9, expected: kConstantCorrectlyRoundedExpectation['f16']['-1.9'] }, // ~-1.9 { input: -1, expected: -1 }, @@ -3444,8 +3450,8 @@ g.test('quantizeToF16Interval') { input: 1, expected: 1 }, { input: 1.9, expected: kConstantCorrectlyRoundedExpectation['f16']['1.9'] }, // ~1.9 { input: kValue.f16.positive.max, expected: kValue.f16.positive.max }, - { input: kValue.f32.positive.max, expected: kUnboundedBounds }, - { input: kValue.f32.positive.infinity, expected: kUnboundedBounds }, + { input: kValue.f32.positive.max, expected: kUnboundedEndpoints }, + { input: kValue.f32.positive.infinity, expected: kUnboundedEndpoints }, ] ) .fn(t => { @@ -3488,35 +3494,21 @@ const kRadiansIntervalCases = { { input: 135, expected: [kMinusOneULPFunctions['f16'](kValue.f16.positive.pi.three_quarters), kPlusOneULPFunctions['f16'](kValue.f16.positive.pi.three_quarters)] }, { input: 180, expected: [kMinusOneULPFunctions['f16'](kValue.f16.positive.pi.whole), kPlusOneULPFunctions['f16'](kValue.f16.positive.pi.whole)] }, ] as ScalarToIntervalCase[], - abstract: [ - { input: -180, expected: kValue.f64.negative.pi.whole }, - { input: -135, expected: kValue.f64.negative.pi.three_quarters }, - { input: -90, expected: kValue.f64.negative.pi.half }, - { input: -60, expected: kValue.f64.negative.pi.third }, - { input: -45, expected: kValue.f64.negative.pi.quarter }, - { input: -30, expected: kValue.f64.negative.pi.sixth }, - { input: 30, expected: kValue.f64.positive.pi.sixth }, - { input: 45, expected: kValue.f64.positive.pi.quarter }, - { input: 60, expected: kValue.f64.positive.pi.third }, - { input: 90, expected: kValue.f64.positive.pi.half }, - { input: 135, expected: kValue.f64.positive.pi.three_quarters }, - { input: 180, expected: kValue.f64.positive.pi.whole }, - ] as ScalarToIntervalCase[], } as const; g.test('radiansInterval') .params(u => u - .combine('trait', ['f32', 'f16', 'abstract'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams<ScalarToIntervalCase>(p => { const trait = p.trait; const constants = FP[trait].constants(); // prettier-ignore return [ - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, { input: 0, expected: 0 }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, ...kRadiansIntervalCases[trait] ]; }) @@ -3536,19 +3528,27 @@ const kRoundIntervalCases = { f32: [ { input: 2 ** 30, expected: 2 ** 30 }, { input: -(2 ** 30), expected: -(2 ** 30) }, - { input: 0x80000000, expected: 0x80000000 }, // https://github.com/gpuweb/cts/issues/2766 + { input: 0x8000_0000, expected: 0x8000_0000 }, // https://github.com/gpuweb/cts/issues/2766 ], f16: [ { input: 2 ** 14, expected: 2 ** 14 }, { input: -(2 ** 14), expected: -(2 ** 14) }, { input: 0x8000, expected: 0x8000 }, // https://github.com/gpuweb/cts/issues/2766 ], + abstract: [ + { input: 2 ** 62, expected: 2 ** 62 }, + { input: -(2 ** 62), expected: -(2 ** 62) }, + { + input: 0x8000_0000_0000_0000, + expected: 0x8000_0000_0000_0000, + }, // https://github.com/gpuweb/cts/issues/2766 + ], } as const; g.test('roundInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams<ScalarToIntervalCase>(p => { const constants = FP[p.trait].constants(); @@ -3571,15 +3571,15 @@ g.test('roundInterval') { input: -1.9, expected: -2 }, // Edge cases - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: constants.positive.max, expected: constants.positive.max }, { input: constants.positive.min, expected: 0 }, { input: constants.negative.min, expected: constants.negative.min }, { input: constants.negative.max, expected: 0 }, ...kRoundIntervalCases[p.trait], - // 32-bit subnormals + // Subnormals { input: constants.positive.subnormal.max, expected: 0 }, { input: constants.positive.subnormal.min, expected: 0 }, { input: constants.negative.subnormal.min, expected: 0 }, @@ -3627,8 +3627,8 @@ g.test('saturateInterval') { input: constants.negative.subnormal.max, expected: [constants.negative.subnormal.max, 0.0] }, // Infinities - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -3651,7 +3651,7 @@ g.test('signInterval') const constants = FP[p.trait].constants(); // prettier-ignore return [ - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: constants.negative.min, expected: -1 }, { input: -10, expected: -1 }, { input: -1, expected: -1 }, @@ -3667,7 +3667,7 @@ g.test('signInterval') { input: 1, expected: 1 }, { input: 10, expected: 1 }, { input: constants.positive.max, expected: 1 }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -3696,13 +3696,13 @@ g.test('sinInterval') // substantially different, so instead of getting 0 you get a value on the // order of 10^-8 away from it, thus difficult to express in a // human-readable manner. - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, { input: constants.negative.pi.half, expected: [-1, kPlusOneULPFunctions[p.trait](-1)] }, { input: 0, expected: 0 }, { input: constants.positive.pi.half, expected: [kMinusOneULPFunctions[p.trait](1), 1] }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -3750,10 +3750,10 @@ g.test('sinhInterval') return [ ...kSinhIntervalCases[p.trait], - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -3832,9 +3832,9 @@ g.test('sqrtInterval') ...kSqrtIntervalCases[p.trait], // Cases out of definition domain - { input: -1, expected: kUnboundedBounds }, - { input: 0, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: -1, expected: kUnboundedEndpoints }, + { input: 0, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -3913,12 +3913,12 @@ g.test('tanInterval') ...kTanIntervalCases[p.trait], // Cases that result in unbounded interval. - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: constants.negative.pi.half, expected: kUnboundedBounds }, - { input: constants.positive.pi.half, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: constants.negative.pi.half, expected: kUnboundedEndpoints }, + { input: constants.positive.pi.half, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -3960,10 +3960,10 @@ g.test('tanhInterval') return [ ...kTanhIntervalCases[p.trait], - { input: constants.negative.infinity, expected: kUnboundedBounds }, - { input: constants.negative.min, expected: kUnboundedBounds }, - { input: constants.positive.max, expected: kUnboundedBounds }, - { input: constants.positive.infinity, expected: kUnboundedBounds }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.min, expected: kUnboundedEndpoints }, + { input: constants.positive.max, expected: kUnboundedEndpoints }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, ]; }) ) @@ -4007,8 +4007,8 @@ g.test('truncInterval') { input: constants.negative.subnormal.max, expected: 0 }, // Edge cases - { input: constants.positive.infinity, expected: kUnboundedBounds }, - { input: constants.negative.infinity, expected: kUnboundedBounds }, + { input: constants.positive.infinity, expected: kUnboundedEndpoints }, + { input: constants.negative.infinity, expected: kUnboundedEndpoints }, { input: constants.positive.max, expected: constants.positive.max }, { input: constants.positive.min, expected: 0 }, { input: constants.negative.min, expected: constants.negative.min }, @@ -4030,7 +4030,7 @@ interface ScalarPairToIntervalCase { // input is a pair of independent values, not a range, so should not be // converted to a FPInterval. input: [number, number]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } // prettier-ignore @@ -4112,14 +4112,14 @@ g.test('additionInterval') { input: [0, constants.negative.subnormal.min], expected: [constants.negative.subnormal.min, 0] }, // Infinities - { input: [0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, + { input: [0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -4226,33 +4226,33 @@ g.test('atan2Interval') // Cases that y out of bound. // positive y, positive x - { input: [Number.POSITIVE_INFINITY, 1], expected: kUnboundedBounds }, + { input: [Number.POSITIVE_INFINITY, 1], expected: kUnboundedEndpoints }, // positive y, negative x - { input: [Number.POSITIVE_INFINITY, -1], expected: kUnboundedBounds }, + { input: [Number.POSITIVE_INFINITY, -1], expected: kUnboundedEndpoints }, // negative y, negative x - { input: [Number.NEGATIVE_INFINITY, -1], expected: kUnboundedBounds }, + { input: [Number.NEGATIVE_INFINITY, -1], expected: kUnboundedEndpoints }, // negative y, positive x - { input: [Number.NEGATIVE_INFINITY, 1], expected: kUnboundedBounds }, + { input: [Number.NEGATIVE_INFINITY, 1], expected: kUnboundedEndpoints }, // Discontinuity @ origin (0,0) - { input: [0, 0], expected: kUnboundedBounds }, - { input: [0, constants.positive.subnormal.max], expected: kUnboundedBounds }, - { input: [0, constants.negative.subnormal.min], expected: kUnboundedBounds }, - { input: [0, constants.positive.min], expected: kUnboundedBounds }, - { input: [0, constants.negative.max], expected: kUnboundedBounds }, - { input: [0, constants.positive.max], expected: kUnboundedBounds }, - { input: [0, constants.negative.min], expected: kUnboundedBounds }, - { input: [0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [0, 1], expected: kUnboundedBounds }, - { input: [constants.positive.subnormal.max, 1], expected: kUnboundedBounds }, - { input: [constants.negative.subnormal.min, 1], expected: kUnboundedBounds }, - - // Very large |x| values should cause kUnboundedBounds to be returned, due to the restrictions on division - { input: [1, constants.positive.max], expected: kUnboundedBounds }, - { input: [1, constants.positive.nearest_max], expected: kUnboundedBounds }, - { input: [1, constants.negative.min], expected: kUnboundedBounds }, - { input: [1, constants.negative.nearest_min], expected: kUnboundedBounds }, + { input: [0, 0], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.subnormal.max], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.subnormal.min], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.min], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.max], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.max], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.min], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [0, 1], expected: kUnboundedEndpoints }, + { input: [constants.positive.subnormal.max, 1], expected: kUnboundedEndpoints }, + { input: [constants.negative.subnormal.min, 1], expected: kUnboundedEndpoints }, + + // Very large |x| values should cause kUnboundedEndpoints to be returned, due to the restrictions on division + { input: [1, constants.positive.max], expected: kUnboundedEndpoints }, + { input: [1, constants.positive.nearest_max], expected: kUnboundedEndpoints }, + { input: [1, constants.negative.min], expected: kUnboundedEndpoints }, + { input: [1, constants.negative.nearest_min], expected: kUnboundedEndpoints }, ]; }) ) @@ -4290,25 +4290,25 @@ g.test('distanceIntervalScalar') { input: [-10.0, 0], expected: kRootSumSquareExpectionInterval[p.trait]['[10]'] }, // ~10 { input: [0, -10.0], expected: kRootSumSquareExpectionInterval[p.trait]['[10]'] }, // ~10 - // distance(x, y), where x - y = 0 has an acceptance interval of kUnboundedBounds, - // because distance(x, y) = length(x - y), and length(0) = kUnboundedBounds - { input: [0, 0], expected: kUnboundedBounds }, - { input: [1.0, 1.0], expected: kUnboundedBounds }, - { input: [-1.0, -1.0], expected: kUnboundedBounds }, + // distance(x, y), where x - y = 0 has an acceptance interval of kUnboundedEndpoints, + // because distance(x, y) = length(x - y), and length(0) = kUnboundedEndpoints + { input: [0, 0], expected: kUnboundedEndpoints }, + { input: [1.0, 1.0], expected: kUnboundedEndpoints }, + { input: [-1.0, -1.0], expected: kUnboundedEndpoints }, // Subnormal Cases - { input: [constants.negative.subnormal.min, 0], expected: kUnboundedBounds }, - { input: [constants.negative.subnormal.max, 0], expected: kUnboundedBounds }, - { input: [constants.positive.subnormal.min, 0], expected: kUnboundedBounds }, - { input: [constants.positive.subnormal.max, 0], expected: kUnboundedBounds }, + { input: [constants.negative.subnormal.min, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.subnormal.max, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.subnormal.min, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.subnormal.max, 0], expected: kUnboundedEndpoints }, // Edge cases - { input: [constants.positive.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.negative.min, 0], expected: kUnboundedBounds }, - { input: [constants.negative.max, 0], expected: kUnboundedBounds }, - { input: [constants.positive.min, 0], expected: kUnboundedBounds }, - { input: [constants.positive.max, 0], expected: kUnboundedBounds }, + { input: [constants.positive.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.min, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.max, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.min, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.max, 0], expected: kUnboundedEndpoints }, ]; }) ) @@ -4371,13 +4371,10 @@ const kDivisionInterval64BitsNormalCases = { g.test('divisionInterval') .params(u => u - .combine('trait', ['abstract', 'f32', 'f16'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams<ScalarPairToIntervalCase>(p => { - // This is a ULP based interval, so abstract should behave like f32, so - // swizzling the trait as needed. - const trait = p.trait === 'abstract' ? 'f32' : p.trait; - const fp = FP[trait]; + const fp = FP[p.trait]; const constants = fp.constants(); // prettier-ignore return [ @@ -4394,26 +4391,23 @@ g.test('divisionInterval') { input: [-4, -2], expected: 2 }, // 64-bit normals that can not be exactly represented - ...kDivisionInterval64BitsNormalCases[trait], + ...kDivisionInterval64BitsNormalCases[p.trait], // Denominator out of range - { input: [1, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [1, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [1, constants.positive.max], expected: kUnboundedBounds }, - { input: [1, constants.negative.min], expected: kUnboundedBounds }, - { input: [1, 0], expected: kUnboundedBounds }, - { input: [1, constants.positive.subnormal.max], expected: kUnboundedBounds }, + { input: [1, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [1, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [1, constants.positive.max], expected: kUnboundedEndpoints }, + { input: [1, constants.negative.min], expected: kUnboundedEndpoints }, + { input: [1, 0], expected: kUnboundedEndpoints }, + { input: [1, constants.positive.subnormal.max], expected: kUnboundedEndpoints }, ]; }) ) .fn(t => { - // This is a ULP based interval, so abstract should behave like f32, so - // swizzling the trait as needed for calculating the expected result. - const trait = t.params.trait === 'abstract' ? 'f32' : t.params.trait; - const fp = FP[trait]; + const fp = FP[t.params.trait]; const error = (n: number): number => { return 2.5 * fp.oneULP(n); @@ -4421,7 +4415,6 @@ g.test('divisionInterval') const [x, y] = t.params.input; - // Do not swizzle here, so the correct implementation under test is called. const expected = FP[t.params.trait].toInterval(applyError(t.params.expected, error)); const got = FP[t.params.trait].divisionInterval(x, y); t.expect( @@ -4452,11 +4445,11 @@ const kLdexpIntervalCases = { // e2 + bias <= 0, expect correctly rounded intervals. { input: [2 ** 120, -130], expected: 2 ** -10 }, // Out of Bounds - { input: [1, 128], expected: kUnboundedBounds }, - { input: [-1, 128], expected: kUnboundedBounds }, - { input: [100, 126], expected: kUnboundedBounds }, - { input: [-100, 126], expected: kUnboundedBounds }, - { input: [2 ** 100, 100], expected: kUnboundedBounds }, + { input: [1, 128], expected: kUnboundedEndpoints }, + { input: [-1, 128], expected: kUnboundedEndpoints }, + { input: [100, 126], expected: kUnboundedEndpoints }, + { input: [-100, 126], expected: kUnboundedEndpoints }, + { input: [2 ** 100, 100], expected: kUnboundedEndpoints }, ] as ScalarPairToIntervalCase[], f16: [ // 64-bit normals @@ -4478,25 +4471,66 @@ const kLdexpIntervalCases = { // e2 + bias <= 0, expect correctly rounded intervals. { input: [2 ** 12, -18], expected: 2 ** -6 }, // Out of Bounds - { input: [1, 16], expected: kUnboundedBounds }, - { input: [-1, 16], expected: kUnboundedBounds }, - { input: [100, 14], expected: kUnboundedBounds }, - { input: [-100, 14], expected: kUnboundedBounds }, - { input: [2 ** 10, 10], expected: kUnboundedBounds }, + { input: [1, 16], expected: kUnboundedEndpoints }, + { input: [-1, 16], expected: kUnboundedEndpoints }, + { input: [100, 14], expected: kUnboundedEndpoints }, + { input: [-100, 14], expected: kUnboundedEndpoints }, + { input: [2 ** 10, 10], expected: kUnboundedEndpoints }, + ] as ScalarPairToIntervalCase[], + abstract: [ + // Edge Cases + // 1.9999999999999997779553950749686919152736663818359375 * 2 ** 1023 = f64.positive.max + { + input: [1.9999999999999997779553950749686919152736663818359375, 1023], + expected: kValue.f64.positive.max, + }, + // f64.positive.min = 1 * 2 ** -1022 + { input: [1, -1022], expected: kValue.f64.positive.min }, + // f64.positive.subnormal.max = 1.9999999999999997779553950749686919152736663818359375 * 2 ** -1022 + { + input: [0.9999999999999997779553950749686919152736663818359375, -1022], + expected: [0, kValue.f64.positive.subnormal.max], + }, + // f64.positive.subnormal.min = 0.0000000000000002220446049250313080847263336181640625 * 2 ** -1022 + { + input: [0.0000000000000002220446049250313080847263336181640625, -1022], + expected: [0, kValue.f64.positive.subnormal.min], + }, + { + input: [-0.0000000000000002220446049250313080847263336181640625, -1022], + expected: [kValue.f64.negative.subnormal.max, 0], + }, + { + input: [-0.9999999999999997779553950749686919152736663818359375, -1022], + expected: [kValue.f64.negative.subnormal.min, 0], + }, + { input: [-1, -1022], expected: kValue.f64.negative.max }, + { + input: [-1.9999999999999997779553950749686919152736663818359375, 1023], + expected: kValue.f64.negative.min, + }, + // e2 + bias <= 0, expect correctly rounded intervals. + { input: [2 ** 120, -130], expected: 2 ** -10 }, + // Out of Bounds + { input: [1, 1024], expected: kUnboundedEndpoints }, + { input: [-1, 1024], expected: kUnboundedEndpoints }, + { input: [100, 1024], expected: kUnboundedEndpoints }, + { input: [-100, 1024], expected: kUnboundedEndpoints }, + { input: [2 ** 100, 1000], expected: kUnboundedEndpoints }, ] as ScalarPairToIntervalCase[], } as const; g.test('ldexpInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams<ScalarPairToIntervalCase>(p => { const trait = FP[p.trait]; const constants = trait.constants(); // prettier-ignore return [ - // always exactly represeantable cases + // always exactly representable cases { input: [0, 0], expected: 0 }, { input: [0, 1], expected: 0 }, { input: [0, -1], expected: 0 }, @@ -4512,8 +4546,8 @@ g.test('ldexpInterval') { input: [constants.positive.max, kValue.i32.negative.min], expected: 0 }, { input: [constants.negative.min, kValue.i32.negative.min], expected: 0 }, // Out of Bounds - { input: [constants.positive.max, kValue.i32.positive.max], expected: kUnboundedBounds }, - { input: [constants.negative.min, kValue.i32.positive.max], expected: kUnboundedBounds }, + { input: [constants.positive.max, kValue.i32.positive.max], expected: kUnboundedEndpoints }, + { input: [constants.negative.min, kValue.i32.positive.max], expected: kUnboundedEndpoints }, ]; }) ) @@ -4572,14 +4606,14 @@ g.test('maxInterval') { input: [constants.negative.subnormal.min, constants.positive.subnormal.max], expected: [constants.negative.subnormal.min, constants.positive.subnormal.max] }, // Infinities - { input: [0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, + { input: [0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -4638,14 +4672,14 @@ g.test('minInterval') { input: [constants.negative.subnormal.min, constants.positive.subnormal.max], expected: [constants.negative.subnormal.min, constants.positive.subnormal.max] }, // Infinities - { input: [0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, + { input: [0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -4740,22 +4774,22 @@ g.test('multiplicationInterval') ...kMultiplicationInterval64BitsNormalCases[p.trait], // Infinities - { input: [0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [1, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [-1, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [1, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [-1, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, + { input: [0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [1, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [-1, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [1, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [-1, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, // Edges - { input: [constants.positive.max, constants.positive.max], expected: kUnboundedBounds }, - { input: [constants.negative.min, constants.negative.min], expected: kUnboundedBounds }, - { input: [constants.positive.max, constants.negative.min], expected: kUnboundedBounds }, - { input: [constants.negative.min, constants.positive.max], expected: kUnboundedBounds }, + { input: [constants.positive.max, constants.positive.max], expected: kUnboundedEndpoints }, + { input: [constants.negative.min, constants.negative.min], expected: kUnboundedEndpoints }, + { input: [constants.positive.max, constants.negative.min], expected: kUnboundedEndpoints }, + { input: [constants.negative.min, constants.positive.max], expected: kUnboundedEndpoints }, ]; }) ) @@ -4808,11 +4842,11 @@ g.test('powInterval') const constants = trait.constants(); // prettier-ignore return [ - { input: [-1, 0], expected: kUnboundedBounds }, - { input: [0, 0], expected: kUnboundedBounds }, - { input: [0, 1], expected: kUnboundedBounds }, - { input: [1, constants.positive.max], expected: kUnboundedBounds }, - { input: [constants.positive.max, 1], expected: kUnboundedBounds }, + { input: [-1, 0], expected: kUnboundedEndpoints }, + { input: [0, 0], expected: kUnboundedEndpoints }, + { input: [0, 1], expected: kUnboundedEndpoints }, + { input: [1, constants.positive.max], expected: kUnboundedEndpoints }, + { input: [constants.positive.max, 1], expected: kUnboundedEndpoints }, ...kPowIntervalCases[p.trait], ]; @@ -4848,7 +4882,7 @@ const kRemainderCases = { g.test('remainderInterval') .params(u => u - .combine('trait', ['abstract', 'f32', 'f16'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams<ScalarPairToIntervalCase>(p => { const trait = kFPTraitForULP[p.trait]; @@ -4878,15 +4912,15 @@ g.test('remainderInterval') { input: [1.125, 1], expected: 0.125 }, // Denominator out of range - { input: [1, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [1, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [1, constants.positive.max], expected: kUnboundedBounds }, - { input: [1, constants.negative.min], expected: kUnboundedBounds }, - { input: [1, 0], expected: kUnboundedBounds }, - { input: [1, constants.positive.subnormal.max], expected: kUnboundedBounds }, + { input: [1, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [1, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [1, constants.positive.max], expected: kUnboundedEndpoints }, + { input: [1, constants.negative.min], expected: kUnboundedEndpoints }, + { input: [1, 0], expected: kUnboundedEndpoints }, + { input: [1, constants.positive.subnormal.max], expected: kUnboundedEndpoints }, ]; }) ) @@ -4904,7 +4938,7 @@ g.test('remainderInterval') g.test('stepInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams<ScalarPairToIntervalCase>(p => { const constants = FP[p.trait].constants(); @@ -4922,12 +4956,17 @@ g.test('stepInterval') { input: [1, -1], expected: 0 }, // 64-bit normals - { input: [0.1, 0.1], expected: [0, 1] }, + // number is f64 internally, so the value representing the literal + // 0.1/-0.1 will always be exactly representable in AbstractFloat, + // since AF is also f64 internally. + // It is impossible with normals to cause the rounding ambiguity that + // causes the 0 or 1 result. + { input: [0.1, 0.1], expected: p.trait === 'abstract' ? 1 : [0, 1] }, { input: [0, 0.1], expected: 1 }, { input: [0.1, 0], expected: 0 }, { input: [0.1, 1], expected: 1 }, { input: [1, 0.1], expected: 0 }, - { input: [-0.1, -0.1], expected: [0, 1] }, + { input: [-0.1, -0.1], expected: p.trait === 'abstract' ? 1 : [0, 1] }, { input: [0, -0.1], expected: 0 }, { input: [-0.1, 0], expected: 1 }, { input: [-0.1, -1], expected: 0 }, @@ -4962,14 +5001,14 @@ g.test('stepInterval') { input: [constants.positive.subnormal.max, constants.negative.subnormal.min], expected: [0, 1] }, // Infinities - { input: [0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, + { input: [0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -5060,14 +5099,14 @@ g.test('subtractionInterval') { input: [0, constants.negative.subnormal.min], expected: [0, constants.positive.subnormal.max] }, // Infinities - { input: [0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 0], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, + { input: [0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 0], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -5084,7 +5123,7 @@ g.test('subtractionInterval') interface ScalarTripleToIntervalCase { input: [number, number, number]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('clampMedianInterval') @@ -5127,10 +5166,10 @@ g.test('clampMedianInterval') { input: [constants.positive.max, constants.positive.max, constants.positive.subnormal.min], expected: constants.positive.max }, // Infinities - { input: [0, 1, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, + { input: [0, 1, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -5185,10 +5224,10 @@ g.test('clampMinMaxInterval') { input: [constants.positive.max, constants.positive.max, constants.positive.subnormal.min], expected: [0, constants.positive.subnormal.min] }, // Infinities - { input: [0, 1, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, + { input: [0, 1, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -5240,21 +5279,12 @@ const kFmaIntervalCases = { // minimum case: -1 * [subnormal ulp] + -1 * [subnormal ulp] rounded to [-2 * [subnormal ulp], 0], // maximum case: -0.0 + -0.0 = 0. { input: [kValue.f16.positive.subnormal.max, kValue.f16.negative.subnormal.min, kValue.f16.negative.subnormal.max], expected: [-2 * FP['f16'].oneULP(0, 'no-flush'), 0] }, ] as ScalarTripleToIntervalCase[], - abstract: [ - // These operations break down in the CTS, because `number` is a f64 under the hood, so precision is sometimes lost - // if intermediate results are closer to 0 than the smallest subnormal will be precisely 0. - // See https://github.com/gpuweb/cts/issues/2993 for details - { input: [kValue.f64.positive.subnormal.max, kValue.f64.positive.subnormal.max, 0], expected: 0 }, - { input: [kValue.f64.positive.subnormal.max, kValue.f64.positive.subnormal.max, kValue.f64.positive.subnormal.max], expected: [0, kValue.f64.positive.subnormal.max] }, - { input: [kValue.f64.positive.subnormal.max, kValue.f64.positive.subnormal.min, kValue.f64.negative.subnormal.max], expected: [kValue.f64.negative.subnormal.max, 0] }, - { input: [kValue.f64.positive.subnormal.max, kValue.f64.negative.subnormal.min, kValue.f64.negative.subnormal.max], expected: [kValue.f64.negative.subnormal.max, 0] }, - ] as ScalarTripleToIntervalCase[], } as const; g.test('fmaInterval') .params(u => u - .combine('trait', ['f32', 'f16', 'abstract'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams<ScalarTripleToIntervalCase>(p => { const trait = FP[p.trait]; @@ -5286,11 +5316,11 @@ g.test('fmaInterval') { input: [0, constants.positive.subnormal.max, constants.positive.subnormal.max], expected: [0, constants.positive.subnormal.max] }, // Infinities - { input: [0, 1, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.max, constants.positive.max, constants.positive.subnormal.min], expected: kUnboundedBounds }, + { input: [0, 1, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.max, constants.positive.max, constants.positive.subnormal.min], expected: kUnboundedEndpoints }, ...kFmaIntervalCases[p.trait], ]; }) @@ -5312,55 +5342,62 @@ g.test('fmaInterval') // prettier-ignore const kMixImpreciseIntervalCases = { f32: [ - // [0.0, 1.0] cases - { input: [0.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0x3fb9_9999_8000_0000n), reinterpretU64AsF64(0x3fb9_9999_a000_0000n)] }, // ~0.1 - { input: [0.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fec_cccc_c000_0000n), reinterpretU64AsF64(0x3fec_cccc_e000_0000n)] }, // ~0.9 - // [1.0, 0.0] cases - { input: [1.0, 0.0, 0.1], expected: [reinterpretU64AsF64(0x3fec_cccc_c000_0000n), reinterpretU64AsF64(0x3fec_cccc_e000_0000n)] }, // ~0.9 - { input: [1.0, 0.0, 0.9], expected: [reinterpretU64AsF64(0x3fb9_9999_0000_0000n), reinterpretU64AsF64(0x3fb9_999a_0000_0000n)] }, // ~0.1 - // [0.0, 10.0] cases - { input: [0.0, 10.0, 0.1], expected: [reinterpretU64AsF64(0x3fef_ffff_e000_0000n), reinterpretU64AsF64(0x3ff0_0000_2000_0000n)] }, // ~1 - { input: [0.0, 10.0, 0.9], expected: [reinterpretU64AsF64(0x4021_ffff_e000_0000n), reinterpretU64AsF64(0x4022_0000_2000_0000n)] }, // ~9 - // [2.0, 10.0] cases - { input: [2.0, 10.0, 0.1], expected: [reinterpretU64AsF64(0x4006_6666_6000_0000n), reinterpretU64AsF64(0x4006_6666_8000_0000n)] }, // ~2.8 - { input: [2.0, 10.0, 0.9], expected: [reinterpretU64AsF64(0x4022_6666_6000_0000n), reinterpretU64AsF64(0x4022_6666_8000_0000n)] }, // ~9.2 - // [-1.0, 1.0] cases - { input: [-1.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0xbfe9_9999_a000_0000n), reinterpretU64AsF64(0xbfe9_9999_8000_0000n)] }, // ~-0.8 - { input: [-1.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fe9_9999_8000_0000n), reinterpretU64AsF64(0x3fe9_9999_c000_0000n)] }, // ~0.8 - - // Showing how precise and imprecise versions diff - // Note that this expectation is 0 only in f32 as 10.0 is much smaller that f32.negative.min, - // So that 10 - f32.negative.min == f32.negative.min even in f64. But for f16, there is not - // a exactly-represenatble f16 value v that make v - f16.negative.min == f16.negative.min - // in f64, in fact that require v being smaller than 2**-37. - { input: [kValue.f32.negative.min, 10.0, 1.0], expected: 0.0 }, - // -10.0 is the same, much smaller than f32.negative.min - { input: [kValue.f32.negative.min, -10.0, 1.0], expected: 0.0 }, + // [0.0, 1.0] cases + { input: [0.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0x3fb9_9999_8000_0000n), reinterpretU64AsF64(0x3fb9_9999_a000_0000n)] }, // ~0.1 + { input: [0.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fec_cccc_c000_0000n), reinterpretU64AsF64(0x3fec_cccc_e000_0000n)] }, // ~0.9 + // [1.0, 0.0] cases + { input: [1.0, 0.0, 0.1], expected: [reinterpretU64AsF64(0x3fec_cccc_c000_0000n), reinterpretU64AsF64(0x3fec_cccc_e000_0000n)] }, // ~0.9 + { input: [1.0, 0.0, 0.9], expected: [reinterpretU64AsF64(0x3fb9_9999_0000_0000n), reinterpretU64AsF64(0x3fb9_999a_0000_0000n)] }, // ~0.1 + // [0.0, 10.0] cases + { input: [0.0, 10.0, 0.1], expected: [reinterpretU64AsF64(0x3fef_ffff_e000_0000n), reinterpretU64AsF64(0x3ff0_0000_2000_0000n)] }, // ~1 + { input: [0.0, 10.0, 0.9], expected: [reinterpretU64AsF64(0x4021_ffff_e000_0000n), reinterpretU64AsF64(0x4022_0000_2000_0000n)] }, // ~9 + // [2.0, 10.0] cases + { input: [2.0, 10.0, 0.1], expected: [reinterpretU64AsF64(0x4006_6666_6000_0000n), reinterpretU64AsF64(0x4006_6666_8000_0000n)] }, // ~2.8 + { input: [2.0, 10.0, 0.9], expected: [reinterpretU64AsF64(0x4022_6666_6000_0000n), reinterpretU64AsF64(0x4022_6666_8000_0000n)] }, // ~9.2 + // [-1.0, 1.0] cases + { input: [-1.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0xbfe9_9999_a000_0000n), reinterpretU64AsF64(0xbfe9_9999_8000_0000n)] }, // ~-0.8 + { input: [-1.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fe9_9999_8000_0000n), reinterpretU64AsF64(0x3fe9_9999_c000_0000n)] }, // ~0.8 + + // Showing how precise and imprecise versions diff + // Note that this expectation is 0 in f32 as |10.0| is much smaller than + // |f32.negative.min|. + // So that 10 - f32.negative.min == -f32.negative.min even in f64. + { input: [kValue.f32.negative.min, 10.0, 1.0], expected: 0.0 }, + // -10.0 is the same, much smaller than f32.negative.min + { input: [kValue.f32.negative.min, -10.0, 1.0], expected: 0.0 }, + { input: [kValue.f32.negative.min, 10.0, 5.0], expected: kUnboundedEndpoints }, + { input: [kValue.f32.negative.min, -10.0, 5.0], expected: kUnboundedEndpoints }, + { input: [kValue.f32.negative.min, 10.0, 0.5], expected: reinterpretU32AsF32(0xfeffffff) }, + { input: [kValue.f32.negative.min, -10.0, 0.5], expected: reinterpretU32AsF32(0xfeffffff) }, ] as ScalarTripleToIntervalCase[], f16: [ - // [0.0, 1.0] cases - { input: [0.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0x3fb9_9800_0000_0000n), reinterpretU64AsF64(0x3fb9_9c00_0000_0000n)] }, // ~0.1 - { input: [0.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fec_cc00_0000_0000n), reinterpretU64AsF64(0x3fec_d000_0000_0000n)] }, // ~0.9 - // [1.0, 0.0] cases - { input: [1.0, 0.0, 0.1], expected: [reinterpretU64AsF64(0x3fec_cc00_0000_0000n), reinterpretU64AsF64(0x3fec_d000_0000_0000n)] }, // ~0.9 - { input: [1.0, 0.0, 0.9], expected: [reinterpretU64AsF64(0x3fb9_8000_0000_0000n), reinterpretU64AsF64(0x3fb9_a000_0000_0000n)] }, // ~0.1 - // [0.0, 10.0] cases - { input: [0.0, 10.0, 0.1], expected: [reinterpretU64AsF64(0x3fef_fc00_0000_0000n), reinterpretU64AsF64(0x3ff0_0400_0000_0000n)] }, // ~1 - { input: [0.0, 10.0, 0.9], expected: [reinterpretU64AsF64(0x4021_fc00_0000_0000n), reinterpretU64AsF64(0x4022_0400_0000_0000n)] }, // ~9 - // [2.0, 10.0] cases - { input: [2.0, 10.0, 0.1], expected: [reinterpretU64AsF64(0x4006_6400_0000_0000n), reinterpretU64AsF64(0x4006_6800_0000_0000n)] }, // ~2.8 - { input: [2.0, 10.0, 0.9], expected: [reinterpretU64AsF64(0x4022_6400_0000_0000n), reinterpretU64AsF64(0x4022_6800_0000_0000n)] }, // ~9.2 - // [-1.0, 1.0] cases - { input: [-1.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0xbfe9_9c00_0000_0000n), reinterpretU64AsF64(0xbfe9_9800_0000_0000n)] }, // ~-0.8 - { input: [-1.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fe9_9800_0000_0000n), reinterpretU64AsF64(0x3fe9_a000_0000_0000n)] }, // ~0.8 - - // Showing how precise and imprecise versions diff - // In imprecise version, we compute (y - x), where y = 10 and x = -65504, the result is 65514 - // and cause an overflow in f16. - { input: [kValue.f16.negative.min, 10.0, 1.0], expected: kUnboundedBounds }, - // (y - x) * 1.0, where y = -10 and x = -65504, the result is 65494 rounded to 65472 or 65504. - // The result is -65504 + 65472 = -32 or -65504 + 65504 = 0. - { input: [kValue.f16.negative.min, -10.0, 1.0], expected: [-32, 0] }, + // [0.0, 1.0] cases + { input: [0.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0x3fb9_9800_0000_0000n), reinterpretU64AsF64(0x3fb9_9c00_0000_0000n)] }, // ~0.1 + { input: [0.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fec_cc00_0000_0000n), reinterpretU64AsF64(0x3fec_d000_0000_0000n)] }, // ~0.9 + // [1.0, 0.0] cases + { input: [1.0, 0.0, 0.1], expected: [reinterpretU64AsF64(0x3fec_cc00_0000_0000n), reinterpretU64AsF64(0x3fec_d000_0000_0000n)] }, // ~0.9 + { input: [1.0, 0.0, 0.9], expected: [reinterpretU64AsF64(0x3fb9_8000_0000_0000n), reinterpretU64AsF64(0x3fb9_a000_0000_0000n)] }, // ~0.1 + // [0.0, 10.0] cases + { input: [0.0, 10.0, 0.1], expected: [reinterpretU64AsF64(0x3fef_fc00_0000_0000n), reinterpretU64AsF64(0x3ff0_0400_0000_0000n)] }, // ~1 + { input: [0.0, 10.0, 0.9], expected: [reinterpretU64AsF64(0x4021_fc00_0000_0000n), reinterpretU64AsF64(0x4022_0400_0000_0000n)] }, // ~9 + // [2.0, 10.0] cases + { input: [2.0, 10.0, 0.1], expected: [reinterpretU64AsF64(0x4006_6400_0000_0000n), reinterpretU64AsF64(0x4006_6800_0000_0000n)] }, // ~2.8 + { input: [2.0, 10.0, 0.9], expected: [reinterpretU64AsF64(0x4022_6400_0000_0000n), reinterpretU64AsF64(0x4022_6800_0000_0000n)] }, // ~9.2 + // [-1.0, 1.0] cases + { input: [-1.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0xbfe9_9c00_0000_0000n), reinterpretU64AsF64(0xbfe9_9800_0000_0000n)] }, // ~-0.8 + { input: [-1.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fe9_9800_0000_0000n), reinterpretU64AsF64(0x3fe9_a000_0000_0000n)] }, // ~0.8 + + // Showing how precise and imprecise versions diff + // In imprecise version, we compute (y - x), where y = 10 and x = -65504, the result is 65514 + // and cause an overflow in f16. + { input: [kValue.f16.negative.min, 10.0, 1.0], expected: kUnboundedEndpoints }, + // (y - x) * 1.0, where y = -10 and x = -65504, the result is 65494 rounded to 65472 or 65504. + // The result is -65504 + 65472 = -32 or -65504 + 65504 = 0. + { input: [kValue.f16.negative.min, -10.0, 1.0], expected: [-32, 0] }, + { input: [kValue.f16.negative.min, 10.0, 5.0], expected: kUnboundedEndpoints }, + { input: [kValue.f16.negative.min, -10.0, 5.0], expected: kUnboundedEndpoints }, + { input: [kValue.f16.negative.min, 10.0, 0.5], expected: kUnboundedEndpoints }, + { input: [kValue.f16.negative.min, -10.0, 0.5], expected: [-32768.0, -32752.0] }, ] as ScalarTripleToIntervalCase[], } as const; @@ -5412,19 +5449,16 @@ g.test('mixImpreciseInterval') { input: [-1.0, 1.0, 2.0], expected: 3.0 }, // Infinities - { input: [0.0, constants.positive.infinity, 0.5], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, 0.0, 0.5], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 1.0, 0.5], expected: kUnboundedBounds }, - { input: [1.0, constants.negative.infinity, 0.5], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity, 0.5], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity, 0.5], expected: kUnboundedBounds }, - { input: [0.0, 1.0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [1.0, 0.0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [0.0, 1.0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [1.0, 0.0, constants.positive.infinity], expected: kUnboundedBounds }, - - // The [negative.min, +/-10.0, 1.0] cases has different result for different trait on - // imprecise version. + { input: [0.0, constants.positive.infinity, 0.5], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, 0.0, 0.5], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 1.0, 0.5], expected: kUnboundedEndpoints }, + { input: [1.0, constants.negative.infinity, 0.5], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity, 0.5], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity, 0.5], expected: kUnboundedEndpoints }, + { input: [0.0, 1.0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [1.0, 0.0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [0.0, 1.0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [1.0, 0.0, constants.positive.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -5459,6 +5493,17 @@ const kMixPreciseIntervalCases = { // [-1.0, 1.0] cases { input: [-1.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0xbfe9_9999_c000_0000n), reinterpretU64AsF64(0xbfe9_9999_8000_0000n)] }, // ~-0.8 { input: [-1.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fe9_9999_8000_0000n), reinterpretU64AsF64(0x3fe9_9999_c000_0000n)] }, // ~0.8 + + // Showing how precise and imprecise versions diff + { input: [kValue.f32.negative.min, 10.0, 1.0], expected: 10 }, + { input: [kValue.f32.negative.min, -10.0, 1.0], expected: -10 }, + { input: [kValue.f32.negative.min, 10.0, 5.0], expected: kUnboundedEndpoints }, + { input: [kValue.f32.negative.min, -10.0, 5.0], expected: kUnboundedEndpoints }, + { input: [kValue.f32.negative.min, 10.0, 0.5], expected: reinterpretU32AsF32(0xfeffffff) }, + { input: [kValue.f32.negative.min, -10.0, 0.5], expected: reinterpretU32AsF32(0xfeffffff) }, + + // Intermediate OOB + { input: [1.0, 2.0, kPlusOneULPFunctions['f32'](kValue.f32.positive.max / 2)], expected: kUnboundedEndpoints }, ] as ScalarTripleToIntervalCase[], f16: [ // [0.0, 1.0] cases @@ -5476,6 +5521,17 @@ const kMixPreciseIntervalCases = { // [-1.0, 1.0] cases { input: [-1.0, 1.0, 0.1], expected: [reinterpretU64AsF64(0xbfe9_a000_0000_0000n), reinterpretU64AsF64(0xbfe9_9800_0000_0000n)] }, // ~-0.8 { input: [-1.0, 1.0, 0.9], expected: [reinterpretU64AsF64(0x3fe9_9800_0000_0000n), reinterpretU64AsF64(0x3fe9_a000_0000_0000n)] }, // ~0.8 + + // Showing how precise and imprecise versions diff + { input: [kValue.f64.negative.min, 10.0, 1.0], expected: kUnboundedEndpoints }, + { input: [kValue.f64.negative.min, -10.0, 1.0], expected: kUnboundedEndpoints }, + { input: [kValue.f64.negative.min, 10.0, 5.0], expected: kUnboundedEndpoints }, + { input: [kValue.f64.negative.min, -10.0, 5.0], expected: kUnboundedEndpoints }, + { input: [kValue.f64.negative.min, 10.0, 0.5], expected: kUnboundedEndpoints }, + { input: [kValue.f64.negative.min, -10.0, 0.5], expected: kUnboundedEndpoints }, + + // Intermediate OOB + { input: [1.0, 2.0, kPlusOneULPFunctions['f16'](kValue.f16.positive.max / 2)], expected: kUnboundedEndpoints }, ] as ScalarTripleToIntervalCase[], } as const; @@ -5527,20 +5583,16 @@ g.test('mixPreciseInterval') { input: [-1.0, 1.0, 2.0], expected: 3.0 }, // Infinities - { input: [0.0, constants.positive.infinity, 0.5], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, 0.0, 0.5], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 1.0, 0.5], expected: kUnboundedBounds }, - { input: [1.0, constants.negative.infinity, 0.5], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, constants.positive.infinity, 0.5], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, constants.negative.infinity, 0.5], expected: kUnboundedBounds }, - { input: [0.0, 1.0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [1.0, 0.0, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [0.0, 1.0, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [1.0, 0.0, constants.positive.infinity], expected: kUnboundedBounds }, - - // Showing how precise and imprecise versions diff - { input: [constants.negative.min, 10.0, 1.0], expected: 10.0 }, - { input: [constants.negative.min, -10.0, 1.0], expected: -10.0 }, + { input: [0.0, constants.positive.infinity, 0.5], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, 0.0, 0.5], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 1.0, 0.5], expected: kUnboundedEndpoints }, + { input: [1.0, constants.negative.infinity, 0.5], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, constants.positive.infinity, 0.5], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, constants.negative.infinity, 0.5], expected: kUnboundedEndpoints }, + { input: [0.0, 1.0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [1.0, 0.0, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [0.0, 1.0, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [1.0, 0.0, constants.positive.infinity], expected: kUnboundedEndpoints }, ]; }) ) @@ -5622,18 +5674,18 @@ g.test('smoothStepInterval') { input: [0, 1, -10], expected: 0 }, // Subnormals - { input: [0, constants.positive.subnormal.max, 1], expected: kUnboundedBounds }, - { input: [0, constants.positive.subnormal.min, 1], expected: kUnboundedBounds }, - { input: [0, constants.negative.subnormal.max, 1], expected: kUnboundedBounds }, - { input: [0, constants.negative.subnormal.min, 1], expected: kUnboundedBounds }, + { input: [0, constants.positive.subnormal.max, 1], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.subnormal.min, 1], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.subnormal.max, 1], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.subnormal.min, 1], expected: kUnboundedEndpoints }, // Infinities - { input: [0, 2, constants.positive.infinity], expected: kUnboundedBounds }, - { input: [0, 2, constants.negative.infinity], expected: kUnboundedBounds }, - { input: [constants.positive.infinity, 2, 1], expected: kUnboundedBounds }, - { input: [constants.negative.infinity, 2, 1], expected: kUnboundedBounds }, - { input: [0, constants.positive.infinity, 1], expected: kUnboundedBounds }, - { input: [0, constants.negative.infinity, 1], expected: kUnboundedBounds }, + { input: [0, 2, constants.positive.infinity], expected: kUnboundedEndpoints }, + { input: [0, 2, constants.negative.infinity], expected: kUnboundedEndpoints }, + { input: [constants.positive.infinity, 2, 1], expected: kUnboundedEndpoints }, + { input: [constants.negative.infinity, 2, 1], expected: kUnboundedEndpoints }, + { input: [0, constants.positive.infinity, 1], expected: kUnboundedEndpoints }, + { input: [0, constants.negative.infinity, 1], expected: kUnboundedEndpoints }, ]; }) ) @@ -5650,7 +5702,7 @@ g.test('smoothStepInterval') interface ScalarToVectorCase { input: number; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } g.test('unpack2x16floatInterval') @@ -5674,8 +5726,8 @@ g.test('unpack2x16floatInterval') { input: 0x000083ff, expected: [[kValue.f16.negative.subnormal.min, 0], 0] }, // f16 out of bounds - { input: 0x7c000000, expected: [kUnboundedBounds, kUnboundedBounds] }, - { input: 0xffff0000, expected: [kUnboundedBounds, kUnboundedBounds] }, + { input: 0x7c000000, expected: [kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: 0xffff0000, expected: [kUnboundedEndpoints, kUnboundedEndpoints] }, ] ) .fn(t => { @@ -5691,24 +5743,24 @@ g.test('unpack2x16floatInterval') // magic numbers that don't pollute the global namespace or have unwieldy long // names. { - const kZeroBounds: IntervalBounds = [ + const kZeroEndpoints: IntervalEndpoints = [ reinterpretU32AsF32(0x81400000), reinterpretU32AsF32(0x01400000), ]; - const kOneBoundsSnorm: IntervalBounds = [ + const kOneEndpointsSnorm: IntervalEndpoints = [ reinterpretU64AsF64(0x3fef_ffff_a000_0000n), reinterpretU64AsF64(0x3ff0_0000_3000_0000n), ]; - const kNegOneBoundsSnorm: IntervalBounds = [ + const kNegOneEndpointsSnorm: IntervalEndpoints = [ reinterpretU64AsF64(0xbff0_0000_3000_0000n), reinterpretU64AsF64(0xbfef_ffff_a000_0000n), ]; - const kHalfBounds2x16snorm: IntervalBounds = [ + const kHalfEndpoints2x16snorm: IntervalEndpoints = [ reinterpretU64AsF64(0x3fe0_001f_a000_0000n), reinterpretU64AsF64(0x3fe0_0020_8000_0000n), ]; // ~0.5..., due to lack of precision in i16 - const kNegHalfBounds2x16snorm: IntervalBounds = [ + const kNegHalfEndpoints2x16snorm: IntervalEndpoints = [ reinterpretU64AsF64(0xbfdf_ffc0_6000_0000n), reinterpretU64AsF64(0xbfdf_ffbf_8000_0000n), ]; // ~-0.5..., due to lack of precision in i16 @@ -5717,13 +5769,13 @@ g.test('unpack2x16floatInterval') .paramsSubcasesOnly<ScalarToVectorCase>( // prettier-ignore [ - { input: 0x00000000, expected: [kZeroBounds, kZeroBounds] }, - { input: 0x00007fff, expected: [kOneBoundsSnorm, kZeroBounds] }, - { input: 0x7fff0000, expected: [kZeroBounds, kOneBoundsSnorm] }, - { input: 0x7fff7fff, expected: [kOneBoundsSnorm, kOneBoundsSnorm] }, - { input: 0x80018001, expected: [kNegOneBoundsSnorm, kNegOneBoundsSnorm] }, - { input: 0x40004000, expected: [kHalfBounds2x16snorm, kHalfBounds2x16snorm] }, - { input: 0xc001c001, expected: [kNegHalfBounds2x16snorm, kNegHalfBounds2x16snorm] }, + { input: 0x00000000, expected: [kZeroEndpoints, kZeroEndpoints] }, + { input: 0x00007fff, expected: [kOneEndpointsSnorm, kZeroEndpoints] }, + { input: 0x7fff0000, expected: [kZeroEndpoints, kOneEndpointsSnorm] }, + { input: 0x7fff7fff, expected: [kOneEndpointsSnorm, kOneEndpointsSnorm] }, + { input: 0x80018001, expected: [kNegOneEndpointsSnorm, kNegOneEndpointsSnorm] }, + { input: 0x40004000, expected: [kHalfEndpoints2x16snorm, kHalfEndpoints2x16snorm] }, + { input: 0xc001c001, expected: [kNegHalfEndpoints2x16snorm, kNegHalfEndpoints2x16snorm] }, ] ) .fn(t => { @@ -5740,15 +5792,15 @@ g.test('unpack2x16floatInterval') // magic numbers that don't pollute the global namespace or have unwieldy long // names. { - const kZeroBounds: IntervalBounds = [ + const kZeroEndpoints: IntervalEndpoints = [ reinterpretU32AsF32(0x8140_0000), reinterpretU32AsF32(0x0140_0000), ]; // ~0 - const kOneBounds: IntervalBounds = [ + const kOneEndpoints: IntervalEndpoints = [ reinterpretU64AsF64(0x3fef_ffff_a000_0000n), reinterpretU64AsF64(0x3ff0_0000_3000_0000n), ]; // ~1 - const kHalfBounds: IntervalBounds = [ + const kHalfEndpoints: IntervalEndpoints = [ reinterpretU64AsF64(0x3fe0_000f_a000_0000n), reinterpretU64AsF64(0x3fe0_0010_8000_0000n), ]; // ~0.5..., due to the lack of accuracy in u16 @@ -5757,11 +5809,11 @@ g.test('unpack2x16floatInterval') .paramsSubcasesOnly<ScalarToVectorCase>( // prettier-ignore [ - { input: 0x00000000, expected: [kZeroBounds, kZeroBounds] }, - { input: 0x0000ffff, expected: [kOneBounds, kZeroBounds] }, - { input: 0xffff0000, expected: [kZeroBounds, kOneBounds] }, - { input: 0xffffffff, expected: [kOneBounds, kOneBounds] }, - { input: 0x80008000, expected: [kHalfBounds, kHalfBounds] }, + { input: 0x00000000, expected: [kZeroEndpoints, kZeroEndpoints] }, + { input: 0x0000ffff, expected: [kOneEndpoints, kZeroEndpoints] }, + { input: 0xffff0000, expected: [kZeroEndpoints, kOneEndpoints] }, + { input: 0xffffffff, expected: [kOneEndpoints, kOneEndpoints] }, + { input: 0x80008000, expected: [kHalfEndpoints, kHalfEndpoints] }, ] ) .fn(t => { @@ -5778,23 +5830,23 @@ g.test('unpack2x16floatInterval') // magic numbers that don't pollute the global namespace or have unwieldy long // names. { - const kZeroBounds: IntervalBounds = [ + const kZeroEndpoints: IntervalEndpoints = [ reinterpretU32AsF32(0x8140_0000), reinterpretU32AsF32(0x0140_0000), ]; // ~0 - const kOneBounds: IntervalBounds = [ + const kOneEndpoints: IntervalEndpoints = [ reinterpretU64AsF64(0x3fef_ffff_a000_0000n), reinterpretU64AsF64(0x3ff0_0000_3000_0000n), ]; // ~1 - const kNegOneBounds: IntervalBounds = [ + const kNegOneEndpoints: IntervalEndpoints = [ reinterpretU64AsF64(0xbff0_0000_3000_0000n), reinterpretU64AsF64(0xbfef_ffff_a0000_000n), ]; // ~-1 - const kHalfBounds: IntervalBounds = [ + const kHalfEndpoints: IntervalEndpoints = [ reinterpretU64AsF64(0x3fe0_2040_2000_0000n), reinterpretU64AsF64(0x3fe0_2041_0000_0000n), ]; // ~0.50196..., due to lack of precision in i8 - const kNegHalfBounds: IntervalBounds = [ + const kNegHalfEndpoints: IntervalEndpoints = [ reinterpretU64AsF64(0xbfdf_bf7f_6000_0000n), reinterpretU64AsF64(0xbfdf_bf7e_8000_0000n), ]; // ~-0.49606..., due to lack of precision in i8 @@ -5803,27 +5855,27 @@ g.test('unpack2x16floatInterval') .paramsSubcasesOnly<ScalarToVectorCase>( // prettier-ignore [ - { input: 0x00000000, expected: [kZeroBounds, kZeroBounds, kZeroBounds, kZeroBounds] }, - { input: 0x0000007f, expected: [kOneBounds, kZeroBounds, kZeroBounds, kZeroBounds] }, - { input: 0x00007f00, expected: [kZeroBounds, kOneBounds, kZeroBounds, kZeroBounds] }, - { input: 0x007f0000, expected: [kZeroBounds, kZeroBounds, kOneBounds, kZeroBounds] }, - { input: 0x7f000000, expected: [kZeroBounds, kZeroBounds, kZeroBounds, kOneBounds] }, - { input: 0x00007f7f, expected: [kOneBounds, kOneBounds, kZeroBounds, kZeroBounds] }, - { input: 0x7f7f0000, expected: [kZeroBounds, kZeroBounds, kOneBounds, kOneBounds] }, - { input: 0x7f007f00, expected: [kZeroBounds, kOneBounds, kZeroBounds, kOneBounds] }, - { input: 0x007f007f, expected: [kOneBounds, kZeroBounds, kOneBounds, kZeroBounds] }, - { input: 0x7f7f7f7f, expected: [kOneBounds, kOneBounds, kOneBounds, kOneBounds] }, + { input: 0x00000000, expected: [kZeroEndpoints, kZeroEndpoints, kZeroEndpoints, kZeroEndpoints] }, + { input: 0x0000007f, expected: [kOneEndpoints, kZeroEndpoints, kZeroEndpoints, kZeroEndpoints] }, + { input: 0x00007f00, expected: [kZeroEndpoints, kOneEndpoints, kZeroEndpoints, kZeroEndpoints] }, + { input: 0x007f0000, expected: [kZeroEndpoints, kZeroEndpoints, kOneEndpoints, kZeroEndpoints] }, + { input: 0x7f000000, expected: [kZeroEndpoints, kZeroEndpoints, kZeroEndpoints, kOneEndpoints] }, + { input: 0x00007f7f, expected: [kOneEndpoints, kOneEndpoints, kZeroEndpoints, kZeroEndpoints] }, + { input: 0x7f7f0000, expected: [kZeroEndpoints, kZeroEndpoints, kOneEndpoints, kOneEndpoints] }, + { input: 0x7f007f00, expected: [kZeroEndpoints, kOneEndpoints, kZeroEndpoints, kOneEndpoints] }, + { input: 0x007f007f, expected: [kOneEndpoints, kZeroEndpoints, kOneEndpoints, kZeroEndpoints] }, + { input: 0x7f7f7f7f, expected: [kOneEndpoints, kOneEndpoints, kOneEndpoints, kOneEndpoints] }, { input: 0x81818181, - expected: [kNegOneBounds, kNegOneBounds, kNegOneBounds, kNegOneBounds] + expected: [kNegOneEndpoints, kNegOneEndpoints, kNegOneEndpoints, kNegOneEndpoints] }, { input: 0x40404040, - expected: [kHalfBounds, kHalfBounds, kHalfBounds, kHalfBounds] + expected: [kHalfEndpoints, kHalfEndpoints, kHalfEndpoints, kHalfEndpoints] }, { input: 0xc1c1c1c1, - expected: [kNegHalfBounds, kNegHalfBounds, kNegHalfBounds, kNegHalfBounds] + expected: [kNegHalfEndpoints, kNegHalfEndpoints, kNegHalfEndpoints, kNegHalfEndpoints] }, ] ) @@ -5841,15 +5893,15 @@ g.test('unpack2x16floatInterval') // magic numbers that don't pollute the global namespace or have unwieldy long // names. { - const kZeroBounds: IntervalBounds = [ + const kZeroEndpoints: IntervalEndpoints = [ reinterpretU32AsF32(0x8140_0000), reinterpretU32AsF32(0x0140_0000), ]; // ~0 - const kOneBounds: IntervalBounds = [ + const kOneEndpoints: IntervalEndpoints = [ reinterpretU64AsF64(0x3fef_ffff_a000_0000n), reinterpretU64AsF64(0x3ff0_0000_3000_0000n), ]; // ~1 - const kHalfBounds: IntervalBounds = [ + const kHalfEndpoints: IntervalEndpoints = [ reinterpretU64AsF64(0x3fe0_100f_a000_0000n), reinterpretU64AsF64(0x3fe0_1010_8000_0000n), ]; // ~0.50196..., due to lack of precision in u8 @@ -5858,19 +5910,19 @@ g.test('unpack2x16floatInterval') .paramsSubcasesOnly<ScalarToVectorCase>( // prettier-ignore [ - { input: 0x00000000, expected: [kZeroBounds, kZeroBounds, kZeroBounds, kZeroBounds] }, - { input: 0x000000ff, expected: [kOneBounds, kZeroBounds, kZeroBounds, kZeroBounds] }, - { input: 0x0000ff00, expected: [kZeroBounds, kOneBounds, kZeroBounds, kZeroBounds] }, - { input: 0x00ff0000, expected: [kZeroBounds, kZeroBounds, kOneBounds, kZeroBounds] }, - { input: 0xff000000, expected: [kZeroBounds, kZeroBounds, kZeroBounds, kOneBounds] }, - { input: 0x0000ffff, expected: [kOneBounds, kOneBounds, kZeroBounds, kZeroBounds] }, - { input: 0xffff0000, expected: [kZeroBounds, kZeroBounds, kOneBounds, kOneBounds] }, - { input: 0xff00ff00, expected: [kZeroBounds, kOneBounds, kZeroBounds, kOneBounds] }, - { input: 0x00ff00ff, expected: [kOneBounds, kZeroBounds, kOneBounds, kZeroBounds] }, - { input: 0xffffffff, expected: [kOneBounds, kOneBounds, kOneBounds, kOneBounds] }, + { input: 0x00000000, expected: [kZeroEndpoints, kZeroEndpoints, kZeroEndpoints, kZeroEndpoints] }, + { input: 0x000000ff, expected: [kOneEndpoints, kZeroEndpoints, kZeroEndpoints, kZeroEndpoints] }, + { input: 0x0000ff00, expected: [kZeroEndpoints, kOneEndpoints, kZeroEndpoints, kZeroEndpoints] }, + { input: 0x00ff0000, expected: [kZeroEndpoints, kZeroEndpoints, kOneEndpoints, kZeroEndpoints] }, + { input: 0xff000000, expected: [kZeroEndpoints, kZeroEndpoints, kZeroEndpoints, kOneEndpoints] }, + { input: 0x0000ffff, expected: [kOneEndpoints, kOneEndpoints, kZeroEndpoints, kZeroEndpoints] }, + { input: 0xffff0000, expected: [kZeroEndpoints, kZeroEndpoints, kOneEndpoints, kOneEndpoints] }, + { input: 0xff00ff00, expected: [kZeroEndpoints, kOneEndpoints, kZeroEndpoints, kOneEndpoints] }, + { input: 0x00ff00ff, expected: [kOneEndpoints, kZeroEndpoints, kOneEndpoints, kZeroEndpoints] }, + { input: 0xffffffff, expected: [kOneEndpoints, kOneEndpoints, kOneEndpoints, kOneEndpoints] }, { input: 0x80808080, - expected: [kHalfBounds, kHalfBounds, kHalfBounds, kHalfBounds] + expected: [kHalfEndpoints, kHalfEndpoints, kHalfEndpoints, kHalfEndpoints] }, ] ) @@ -5886,7 +5938,7 @@ g.test('unpack2x16floatInterval') interface VectorToIntervalCase { input: number[]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('lengthIntervalVector') @@ -5926,10 +5978,10 @@ g.test('lengthIntervalVector') {input: [-1.0, 1.0, -1.0, 1.0], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0, 1.0, 1.0, 1.0]'] }, // ~2 {input: [0.1, 0.0, 0.0, 0.0], expected: kRootSumSquareExpectionInterval[p.trait]['[0.1]'] }, // ~0.1 - // Test that dot going OOB bounds in the intermediate calculations propagates - { input: [constants.positive.nearest_max, constants.positive.max, constants.negative.min], expected: kUnboundedBounds }, - { input: [constants.positive.max, constants.positive.nearest_max, constants.negative.min], expected: kUnboundedBounds }, - { input: [constants.negative.min, constants.positive.max, constants.positive.nearest_max], expected: kUnboundedBounds }, + // Test that dot going OOB in the intermediate calculations propagates + { input: [constants.positive.nearest_max, constants.positive.max, constants.negative.min], expected: kUnboundedEndpoints }, + { input: [constants.positive.max, constants.positive.nearest_max, constants.negative.min], expected: kUnboundedEndpoints }, + { input: [constants.negative.min, constants.positive.max, constants.positive.nearest_max], expected: kUnboundedEndpoints }, ]; }) ) @@ -5945,7 +5997,7 @@ g.test('lengthIntervalVector') interface VectorPairToIntervalCase { input: [number[], number[]]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('distanceIntervalVector') @@ -5956,11 +6008,11 @@ g.test('distanceIntervalVector') .expandWithParams<VectorPairToIntervalCase>(p => { // prettier-ignore return [ - // distance(x, y), where x - y = 0 has an acceptance interval of kUnboundedBounds, - // because distance(x, y) = length(x - y), and length(0) = kUnboundedBounds. + // distance(x, y), where x - y = 0 has an acceptance interval of kUnboundedEndpoints, + // because distance(x, y) = length(x - y), and length(0) = kUnboundedEndpoints. // vec2 - { input: [[1.0, 0.0], [1.0, 0.0]], expected: kUnboundedBounds }, + { input: [[1.0, 0.0], [1.0, 0.0]], expected: kUnboundedEndpoints }, { input: [[1.0, 0.0], [0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 { input: [[0.0, 0.0], [1.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 { input: [[-1.0, 0.0], [0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 @@ -5969,7 +6021,7 @@ g.test('distanceIntervalVector') { input: [[0.1, 0.0], [0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[0.1]'] }, // ~0.1 // vec3 - { input: [[1.0, 0.0, 0.0], [1.0, 0.0, 0.0]], expected: kUnboundedBounds }, + { input: [[1.0, 0.0, 0.0], [1.0, 0.0, 0.0]], expected: kUnboundedEndpoints }, { input: [[1.0, 0.0, 0.0], [0.0, 0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 { input: [[0.0, 1.0, 0.0], [0.0, 0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 { input: [[0.0, 0.0, 1.0], [0.0, 0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 @@ -5984,7 +6036,7 @@ g.test('distanceIntervalVector') { input: [[0.0, 0.0, 0.0], [0.1, 0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[0.1]'] }, // ~0.1 // vec4 - { input: [[1.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]], expected: kUnboundedBounds }, + { input: [[1.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]], expected: kUnboundedEndpoints }, { input: [[1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 { input: [[0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 { input: [[0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0]], expected: kRootSumSquareExpectionInterval[p.trait]['[1.0]'] }, // ~1 @@ -6019,7 +6071,7 @@ const kDotIntervalCases = { // 3.0*3.0 = 9.0 is much smaller than kValue.f32.positive.max, as a result // kValue.f32.positive.max + 9.0 = kValue.f32.positive.max in f32 and even f64. So, if the // positive and negative large number cancel each other first, the result would be - // 2.0*2.0+3.0*3.0 = 13. Otherwise, the resule would be 0.0 or 4.0 or 9.0. + // 2.0*2.0+3.0*3.0 = 13. Otherwise, the result would be 0.0 or 4.0 or 9.0. // https://github.com/gpuweb/cts/issues/2155 { input: [[kValue.f32.positive.max, 1.0, 2.0, 3.0], [-1.0, kValue.f32.positive.max, -2.0, -3.0]], expected: [-13, 0] }, { input: [[kValue.f32.positive.max, 1.0, 2.0, 3.0], [1.0, kValue.f32.negative.min, 2.0, 3.0]], expected: [0, 13] }, @@ -6029,10 +6081,10 @@ const kDotIntervalCases = { // 3.0*3.0 = 9.0 is not small enough comparing to kValue.f16.positive.max = 65504, as a result // kValue.f16.positive.max + 9.0 = 65513 is exactly representable in f32 and f64. So, if the // positive and negative large number don't cancel each other first, the computation will - // overflow f16 and result in unbounded bounds. + // overflow f16 and result in unbounded endpoints. // https://github.com/gpuweb/cts/issues/2155 - { input: [[kValue.f16.positive.max, 1.0, 2.0, 3.0], [-1.0, kValue.f16.positive.max, -2.0, -3.0]], expected: kUnboundedBounds }, - { input: [[kValue.f16.positive.max, 1.0, 2.0, 3.0], [1.0, kValue.f16.negative.min, 2.0, 3.0]], expected: kUnboundedBounds }, + { input: [[kValue.f16.positive.max, 1.0, 2.0, 3.0], [-1.0, kValue.f16.positive.max, -2.0, -3.0]], expected: kUnboundedEndpoints }, + { input: [[kValue.f16.positive.max, 1.0, 2.0, 3.0], [1.0, kValue.f16.negative.min, 2.0, 3.0]], expected: kUnboundedEndpoints }, ] as VectorPairToIntervalCase[], } as const; @@ -6052,7 +6104,7 @@ g.test('dotInterval') { input: [[1.0, 1.0], [1.0, 1.0]], expected: 2.0 }, { input: [[-1.0, -1.0], [-1.0, -1.0]], expected: 2.0 }, { input: [[-1.0, 1.0], [1.0, -1.0]], expected: -2.0 }, - { input: [[0.1, 0.0], [1.0, 0.0]], expected: kConstantCorrectlyRoundedExpectation[p.trait]['0.1']}, // correclt rounded of 0.1 + { input: [[0.1, 0.0], [1.0, 0.0]], expected: kConstantCorrectlyRoundedExpectation[p.trait]['0.1']}, // correctly rounded of 0.1 // vec3 { input: [[1.0, 0.0, 0.0], [1.0, 0.0, 0.0]], expected: 1.0 }, @@ -6061,7 +6113,7 @@ g.test('dotInterval') { input: [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], expected: 3.0 }, { input: [[-1.0, -1.0, -1.0], [-1.0, -1.0, -1.0]], expected: 3.0 }, { input: [[1.0, -1.0, -1.0], [-1.0, 1.0, -1.0]], expected: -1.0 }, - { input: [[0.1, 0.0, 0.0], [1.0, 0.0, 0.0]], expected: kConstantCorrectlyRoundedExpectation[p.trait]['0.1']}, // correclt rounded of 0.1 + { input: [[0.1, 0.0, 0.0], [1.0, 0.0, 0.0]], expected: kConstantCorrectlyRoundedExpectation[p.trait]['0.1']}, // correctly rounded of 0.1 // vec4 { input: [[1.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]], expected: 1.0 }, @@ -6076,12 +6128,12 @@ g.test('dotInterval') ...kDotIntervalCases[p.trait], // Test that going out of bounds in the intermediate calculations is caught correctly. - { input: [[constants.positive.nearest_max, constants.positive.max, constants.negative.min], [1.0, 1.0, 1.0]], expected: kUnboundedBounds }, - { input: [[constants.positive.nearest_max, constants.negative.min, constants.positive.max], [1.0, 1.0, 1.0]], expected: kUnboundedBounds }, - { input: [[constants.positive.max, constants.positive.nearest_max, constants.negative.min], [1.0, 1.0, 1.0]], expected: kUnboundedBounds }, - { input: [[constants.negative.min, constants.positive.nearest_max, constants.positive.max], [1.0, 1.0, 1.0]], expected: kUnboundedBounds }, - { input: [[constants.positive.max, constants.negative.min, constants.positive.nearest_max], [1.0, 1.0, 1.0]], expected: kUnboundedBounds }, - { input: [[constants.negative.min, constants.positive.max, constants.positive.nearest_max], [1.0, 1.0, 1.0]], expected: kUnboundedBounds }, + { input: [[constants.positive.nearest_max, constants.positive.max, constants.negative.min], [1.0, 1.0, 1.0]], expected: kUnboundedEndpoints }, + { input: [[constants.positive.nearest_max, constants.negative.min, constants.positive.max], [1.0, 1.0, 1.0]], expected: kUnboundedEndpoints }, + { input: [[constants.positive.max, constants.positive.nearest_max, constants.negative.min], [1.0, 1.0, 1.0]], expected: kUnboundedEndpoints }, + { input: [[constants.negative.min, constants.positive.nearest_max, constants.positive.max], [1.0, 1.0, 1.0]], expected: kUnboundedEndpoints }, + { input: [[constants.positive.max, constants.negative.min, constants.positive.nearest_max], [1.0, 1.0, 1.0]], expected: kUnboundedEndpoints }, + { input: [[constants.negative.min, constants.positive.max, constants.positive.nearest_max], [1.0, 1.0, 1.0]], expected: kUnboundedEndpoints }, ]; }) ) @@ -6098,54 +6150,54 @@ g.test('dotInterval') interface VectorToVectorCase { input: number[]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } // prettier-ignore const kNormalizeIntervalCases = { f32: [ // vec2 - {input: [1.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0] - {input: [0.0, 1.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)]] }, // [ ~0.0, ~1.0] - {input: [-1.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_0000_b000_0000n), reinterpretU64AsF64(0xbfef_fffe_7000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0] - {input: [1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fe6_a09d_5000_0000n), reinterpretU64AsF64(0x3fe6_a09f_9000_0000n)], [reinterpretU64AsF64(0x3fe6_a09d_5000_0000n), reinterpretU64AsF64(0x3fe6_a09f_9000_0000n)]] }, // [ ~1/√2, ~1/√2] + { input: [1.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0] + { input: [0.0, 1.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)]] }, // [ ~0.0, ~1.0] + { input: [-1.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_0000_b000_0000n), reinterpretU64AsF64(0xbfef_fffe_7000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0] + { input: [1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fe6_a09d_5000_0000n), reinterpretU64AsF64(0x3fe6_a09f_9000_0000n)], [reinterpretU64AsF64(0x3fe6_a09d_5000_0000n), reinterpretU64AsF64(0x3fe6_a09f_9000_0000n)]] }, // [ ~1/√2, ~1/√2] // vec3 - {input: [1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0, ~0.0] - {input: [0.0, 1.0, 0.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~0.0, ~1.0, ~0.0] - {input: [0.0, 0.0, 1.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)]] }, // [ ~0.0, ~0.0, ~1.0] - {input: [-1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_0000_b000_0000n), reinterpretU64AsF64(0xbfef_fffe_7000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0, ~0.0] - {input: [1.0, 1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fe2_79a6_5000_0000n), reinterpretU64AsF64(0x3fe2_79a8_5000_0000n)], [reinterpretU64AsF64(0x3fe2_79a6_5000_0000n), reinterpretU64AsF64(0x3fe2_79a8_5000_0000n)], [reinterpretU64AsF64(0x3fe2_79a6_5000_0000n), reinterpretU64AsF64(0x3fe2_79a8_5000_0000n)]] }, // [ ~1/√3, ~1/√3, ~1/√3] + { input: [1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0, ~0.0] + { input: [0.0, 1.0, 0.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~0.0, ~1.0, ~0.0] + { input: [0.0, 0.0, 1.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)]] }, // [ ~0.0, ~0.0, ~1.0] + { input: [-1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_0000_b000_0000n), reinterpretU64AsF64(0xbfef_fffe_7000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0, ~0.0] + { input: [1.0, 1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fe2_79a6_5000_0000n), reinterpretU64AsF64(0x3fe2_79a8_5000_0000n)], [reinterpretU64AsF64(0x3fe2_79a6_5000_0000n), reinterpretU64AsF64(0x3fe2_79a8_5000_0000n)], [reinterpretU64AsF64(0x3fe2_79a6_5000_0000n), reinterpretU64AsF64(0x3fe2_79a8_5000_0000n)]] }, // [ ~1/√3, ~1/√3, ~1/√3] // vec4 - {input: [1.0, 0.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0, ~0.0, ~0.0] - {input: [0.0, 1.0, 0.0, 0.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~0.0, ~1.0, ~0.0, ~0.0] - {input: [0.0, 0.0, 1.0, 0.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~0.0, ~0.0, ~1.0, ~0.0] - {input: [0.0, 0.0, 0.0, 1.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)]] }, // [ ~0.0, ~0.0, ~0.0, ~1.0] - {input: [-1.0, 0.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_0000_b000_0000n), reinterpretU64AsF64(0xbfef_fffe_7000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0, ~0.0, ~0.0] - {input: [1.0, 1.0, 1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fdf_fffe_7000_0000n), reinterpretU64AsF64(0x3fe0_0000_b000_0000n)], [reinterpretU64AsF64(0x3fdf_fffe_7000_0000n), reinterpretU64AsF64(0x3fe0_0000_b000_0000n)], [reinterpretU64AsF64(0x3fdf_fffe_7000_0000n), reinterpretU64AsF64(0x3fe0_0000_b000_0000n)], [reinterpretU64AsF64(0x3fdf_fffe_7000_0000n), reinterpretU64AsF64(0x3fe0_0000_b000_0000n)]] }, // [ ~1/√4, ~1/√4, ~1/√4] + { input: [1.0, 0.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0, ~0.0, ~0.0] + { input: [0.0, 1.0, 0.0, 0.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~0.0, ~1.0, ~0.0, ~0.0] + { input: [0.0, 0.0, 1.0, 0.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~0.0, ~0.0, ~1.0, ~0.0] + { input: [0.0, 0.0, 0.0, 1.0], expected: [[reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU64AsF64(0x3fef_fffe_7000_0000n), reinterpretU64AsF64(0x3ff0_0000_b000_0000n)]] }, // [ ~0.0, ~0.0, ~0.0, ~1.0] + { input: [-1.0, 0.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_0000_b000_0000n), reinterpretU64AsF64(0xbfef_fffe_7000_0000n)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)], [reinterpretU32AsF32(0x81200000), reinterpretU32AsF32(0x01200000)]] }, // [ ~1.0, ~0.0, ~0.0, ~0.0] + { input: [1.0, 1.0, 1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fdf_fffe_7000_0000n), reinterpretU64AsF64(0x3fe0_0000_b000_0000n)], [reinterpretU64AsF64(0x3fdf_fffe_7000_0000n), reinterpretU64AsF64(0x3fe0_0000_b000_0000n)], [reinterpretU64AsF64(0x3fdf_fffe_7000_0000n), reinterpretU64AsF64(0x3fe0_0000_b000_0000n)], [reinterpretU64AsF64(0x3fdf_fffe_7000_0000n), reinterpretU64AsF64(0x3fe0_0000_b000_0000n)]] }, // [ ~1/√4, ~1/√4, ~1/√4] ] as VectorToVectorCase[], f16: [ // vec2 - {input: [1.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0] - {input: [0.0, 1.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)]] }, // [ ~0.0, ~1.0] - {input: [-1.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_1600_0000_0000n), reinterpretU64AsF64(0xbfef_ce00_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0] - {input: [1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fe6_7e00_0000_0000n), reinterpretU64AsF64(0x3fe6_c600_0000_0000n)], [reinterpretU64AsF64(0x3fe6_7e00_0000_0000n), reinterpretU64AsF64(0x3fe6_c600_0000_0000n)]] }, // [ ~1/√2, ~1/√2] + { input: [1.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0] + { input: [0.0, 1.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)]] }, // [ ~0.0, ~1.0] + { input: [-1.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_1600_0000_0000n), reinterpretU64AsF64(0xbfef_ce00_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0] + { input: [1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fe6_7e00_0000_0000n), reinterpretU64AsF64(0x3fe6_c600_0000_0000n)], [reinterpretU64AsF64(0x3fe6_7e00_0000_0000n), reinterpretU64AsF64(0x3fe6_c600_0000_0000n)]] }, // [ ~1/√2, ~1/√2] // vec3 - {input: [1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0, ~0.0] - {input: [0.0, 1.0, 0.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~0.0, ~1.0, ~0.0] - {input: [0.0, 0.0, 1.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)]] }, // [ ~0.0, ~0.0, ~1.0] - {input: [-1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_1600_0000_0000n), reinterpretU64AsF64(0xbfef_ce00_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0, ~0.0] - {input: [1.0, 1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fe2_5a00_0000_0000n), reinterpretU64AsF64(0x3fe2_9a00_0000_0000n)], [reinterpretU64AsF64(0x3fe2_5a00_0000_0000n), reinterpretU64AsF64(0x3fe2_9a00_0000_0000n)], [reinterpretU64AsF64(0x3fe2_5a00_0000_0000n), reinterpretU64AsF64(0x3fe2_9a00_0000_0000n)]] }, // [ ~1/√3, ~1/√3, ~1/√3] + { input: [1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0, ~0.0] + { input: [0.0, 1.0, 0.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~0.0, ~1.0, ~0.0] + { input: [0.0, 0.0, 1.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)]] }, // [ ~0.0, ~0.0, ~1.0] + { input: [-1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_1600_0000_0000n), reinterpretU64AsF64(0xbfef_ce00_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0, ~0.0] + { input: [1.0, 1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fe2_5a00_0000_0000n), reinterpretU64AsF64(0x3fe2_9a00_0000_0000n)], [reinterpretU64AsF64(0x3fe2_5a00_0000_0000n), reinterpretU64AsF64(0x3fe2_9a00_0000_0000n)], [reinterpretU64AsF64(0x3fe2_5a00_0000_0000n), reinterpretU64AsF64(0x3fe2_9a00_0000_0000n)]] }, // [ ~1/√3, ~1/√3, ~1/√3] // vec4 - {input: [1.0, 0.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0, ~0.0, ~0.0] - {input: [0.0, 1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~0.0, ~1.0, ~0.0, ~0.0] - {input: [0.0, 0.0, 1.0, 0.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~0.0, ~0.0, ~1.0, ~0.0] - {input: [0.0, 0.0, 0.0, 1.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)]] }, // [ ~0.0, ~0.0, ~0.0, ~1.0] - {input: [-1.0, 0.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_1600_0000_0000n), reinterpretU64AsF64(0xbfef_ce00_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0, ~0.0, ~0.0] - {input: [1.0, 1.0, 1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fdf_ce00_0000_0000n), reinterpretU64AsF64(0x3fe0_1600_0000_0000n)], [reinterpretU64AsF64(0x3fdf_ce00_0000_0000n), reinterpretU64AsF64(0x3fe0_1600_0000_0000n)], [reinterpretU64AsF64(0x3fdf_ce00_0000_0000n), reinterpretU64AsF64(0x3fe0_1600_0000_0000n)], [reinterpretU64AsF64(0x3fdf_ce00_0000_0000n), reinterpretU64AsF64(0x3fe0_1600_0000_0000n)]] }, // [ ~1/√4, ~1/√4, ~1/√4] + { input: [1.0, 0.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0, ~0.0, ~0.0] + { input: [0.0, 1.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~0.0, ~1.0, ~0.0, ~0.0] + { input: [0.0, 0.0, 1.0, 0.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~0.0, ~0.0, ~1.0, ~0.0] + { input: [0.0, 0.0, 0.0, 1.0], expected: [[reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0x3fef_ce00_0000_0000n), reinterpretU64AsF64(0x3ff0_1600_0000_0000n)]] }, // [ ~0.0, ~0.0, ~0.0, ~1.0] + { input: [-1.0, 0.0, 0.0, 0.0], expected: [[reinterpretU64AsF64(0xbff0_1600_0000_0000n), reinterpretU64AsF64(0xbfef_ce00_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)], [reinterpretU64AsF64(0xbf24_0000_0000_0000n), reinterpretU64AsF64(0x3f24_0000_0000_0000n)]] }, // [ ~1.0, ~0.0, ~0.0, ~0.0] + { input: [1.0, 1.0, 1.0, 1.0], expected: [[reinterpretU64AsF64(0x3fdf_ce00_0000_0000n), reinterpretU64AsF64(0x3fe0_1600_0000_0000n)], [reinterpretU64AsF64(0x3fdf_ce00_0000_0000n), reinterpretU64AsF64(0x3fe0_1600_0000_0000n)], [reinterpretU64AsF64(0x3fdf_ce00_0000_0000n), reinterpretU64AsF64(0x3fe0_1600_0000_0000n)], [reinterpretU64AsF64(0x3fdf_ce00_0000_0000n), reinterpretU64AsF64(0x3fe0_1600_0000_0000n)]] }, // [ ~1/√4, ~1/√4, ~1/√4] ] as VectorToVectorCase[], } as const; @@ -6154,7 +6206,20 @@ g.test('normalizeInterval') u .combine('trait', ['f32', 'f16'] as const) .beginSubcases() - .expandWithParams<VectorToVectorCase>(p => kNormalizeIntervalCases[p.trait]) + .expandWithParams<VectorToVectorCase>(p => { + const trait = FP[p.trait]; + const constants = trait.constants(); + // prettier-ignore + return [ + ...kNormalizeIntervalCases[p.trait], + + // Very small vectors go OOB due to division + { input: [constants.positive.subnormal.max, constants.positive.subnormal.max], expected: [kUnboundedEndpoints, kUnboundedEndpoints], }, + + // Very large vectors go OOB due to overflow + { input: [constants.positive.max, constants.positive.max], expected: [kUnboundedEndpoints, kUnboundedEndpoints], }, + ]; + }) ) .fn(t => { const x = t.params.input; @@ -6169,7 +6234,7 @@ g.test('normalizeInterval') interface VectorPairToVectorCase { input: [number[], number[]]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } // prettier-ignore @@ -6218,30 +6283,12 @@ const kCrossIntervalCases = { ] }, ] as VectorPairToVectorCase[], - abstract: [ - { input: [ - [kValue.f64.positive.subnormal.max, kValue.f64.negative.subnormal.max, kValue.f64.negative.subnormal.min], - [kValue.f64.negative.subnormal.min, kValue.f64.positive.subnormal.min, kValue.f64.negative.subnormal.max] - ], - expected: [0.0, 0.0, 0.0] - }, - { input: [ - [0.1, -0.1, -0.1], - [-0.1, 0.1, -0.1] - ], - expected: [ - reinterpretU64AsF64(0x3f94_7ae1_47ae_147cn), // ~0.02 - reinterpretU64AsF64(0x3f94_7ae1_47ae_147cn), // ~0.02 - 0.0 - ] - }, - ] as VectorPairToVectorCase[], } as const; g.test('crossInterval') .params(u => u - .combine('trait', ['f32', 'f16', 'abstract'] as const) + .combine('trait', ['f32', 'f16'] as const) .beginSubcases() .expandWithParams<VectorPairToVectorCase>(p => { const trait = FP[p.trait]; @@ -6261,6 +6308,9 @@ g.test('crossInterval') { input: [[1.0, -1.0, -1.0], [-1.0, 1.0, -1.0]], expected: [2.0, 2.0, 0.0] }, { input: [[1.0, 2, 3], [1.0, 5.0, 7.0]], expected: [-1, -4, 3] }, ...kCrossIntervalCases[p.trait], + + // OOB + { input: [[constants.positive.max, 1.0, 1.0], [1.0, constants.positive.max, -1.0]], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, ]; }) ) @@ -6320,6 +6370,7 @@ g.test('reflectInterval') { input: [[0.0, 1.0], [1.0, 0.0]], expected: [0.0, 1.0] }, { input: [[1.0, 1.0], [1.0, 1.0]], expected: [-3.0, -3.0] }, { input: [[-1.0, -1.0], [1.0, 1.0]], expected: [3.0, 3.0] }, + // vec3s { input: [[1.0, 0.0, 0.0], [1.0, 0.0, 0.0]], expected: [-1.0, 0.0, 0.0] }, { input: [[0.0, 1.0, 0.0], [1.0, 0.0, 0.0]], expected: [0.0, 1.0, 0.0] }, @@ -6328,6 +6379,7 @@ g.test('reflectInterval') { input: [[1.0, 0.0, 0.0], [0.0, 0.0, 1.0]], expected: [1.0, 0.0, 0.0] }, { input: [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]], expected: [-5.0, -5.0, -5.0] }, { input: [[-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]], expected: [5.0, 5.0, 5.0] }, + // vec4s { input: [[1.0, 0.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]], expected: [-1.0, 0.0, 0.0, 0.0] }, { input: [[0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0]], expected: [0.0, 1.0, 0.0, 0.0] }, @@ -6337,16 +6389,17 @@ g.test('reflectInterval') { input: [[1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]], expected: [1.0, 0.0, 0.0, 0.0] }, { input: [[1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0]], expected: [1.0, 0.0, 0.0, 0.0] }, { input: [[-1.0, -1.0, -1.0, -1.0], [1.0, 1.0, 1.0, 1.0]], expected: [7.0, 7.0, 7.0, 7.0] }, - // Test that dot going OOB bounds in the intermediate calculations propagates - { input: [[constants.positive.nearest_max, constants.positive.max, constants.negative.min], [1.0, 1.0, 1.0]], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.positive.nearest_max, constants.negative.min, constants.positive.max], [1.0, 1.0, 1.0]], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.positive.max, constants.positive.nearest_max, constants.negative.min], [1.0, 1.0, 1.0]], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.negative.min, constants.positive.nearest_max, constants.positive.max], [1.0, 1.0, 1.0]], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.positive.max, constants.negative.min, constants.positive.nearest_max], [1.0, 1.0, 1.0]], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.negative.min, constants.positive.max, constants.positive.nearest_max], [1.0, 1.0, 1.0]], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, + + // Test that dot going OOB in the intermediate calculations propagates + { input: [[constants.positive.nearest_max, constants.positive.max, constants.negative.min], [1.0, 1.0, 1.0]], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.positive.nearest_max, constants.negative.min, constants.positive.max], [1.0, 1.0, 1.0]], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.positive.max, constants.positive.nearest_max, constants.negative.min], [1.0, 1.0, 1.0]], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.negative.min, constants.positive.nearest_max, constants.positive.max], [1.0, 1.0, 1.0]], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.positive.max, constants.negative.min, constants.positive.nearest_max], [1.0, 1.0, 1.0]], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.negative.min, constants.positive.max, constants.positive.nearest_max], [1.0, 1.0, 1.0]], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, // Test that post-dot going OOB propagates - { input: [[constants.positive.max, 1.0, 2.0, 3.0], [-1.0, constants.positive.max, -2.0, -3.0]], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, + { input: [[constants.positive.max, 1.0, 2.0, 3.0], [-1.0, constants.positive.max, -2.0, -3.0]], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, ]; }) ) @@ -6365,7 +6418,7 @@ g.test('reflectInterval') interface MatrixToScalarCase { input: number[][]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('determinantInterval') @@ -6480,7 +6533,7 @@ g.test('determinantInterval') interface MatrixToMatrixCase { input: number[][]; - expected: (number | IntervalBounds)[][]; + expected: (number | IntervalEndpoints)[][]; } g.test('transposeInterval') @@ -6634,7 +6687,7 @@ g.test('transposeInterval') interface MatrixPairToMatrixCase { input: [number[][], number[][]]; - expected: (number | IntervalBounds)[][]; + expected: (number | IntervalEndpoints)[][]; } g.test('additionMatrixMatrixInterval') @@ -6642,184 +6695,205 @@ g.test('additionMatrixMatrixInterval') u .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() - .combineWithParams<MatrixPairToMatrixCase>([ - // Only testing that different shapes of matrices are handled correctly - // here, to reduce test duplication. - // additionMatrixMatrixInterval uses AdditionIntervalOp for calculating intervals, - // so the testing for additionInterval covers the actual interval - // calculations. - { - input: [ - [ - [1, 2], - [3, 4], + .expandWithParams<MatrixPairToMatrixCase>(p => { + const trait = FP[p.trait]; + const constants = trait.constants(); + return [ + // Only testing that different shapes of matrices are handled correctly + // here, to reduce test duplication. + // additionMatrixMatrixInterval uses AdditionIntervalOp for calculating intervals, + // so the testing for additionInterval covers the actual interval + // calculations. + { + input: [ + [ + [1, 2], + [3, 4], + ], + [ + [10, 20], + [30, 40], + ], ], - [ - [10, 20], - [30, 40], + expected: [ + [11, 22], + [33, 44], ], - ], - expected: [ - [11, 22], - [33, 44], - ], - }, - { - input: [ - [ - [1, 2], - [3, 4], - [5, 6], + }, + { + input: [ + [ + [1, 2], + [3, 4], + [5, 6], + ], + [ + [10, 20], + [30, 40], + [50, 60], + ], ], - [ - [10, 20], - [30, 40], - [50, 60], + expected: [ + [11, 22], + [33, 44], + [55, 66], ], - ], - expected: [ - [11, 22], - [33, 44], - [55, 66], - ], - }, - { - input: [ - [ - [1, 2], - [3, 4], - [5, 6], - [7, 8], + }, + { + input: [ + [ + [1, 2], + [3, 4], + [5, 6], + [7, 8], + ], + [ + [10, 20], + [30, 40], + [50, 60], + [70, 80], + ], ], - [ - [10, 20], - [30, 40], - [50, 60], - [70, 80], + expected: [ + [11, 22], + [33, 44], + [55, 66], + [77, 88], ], - ], - expected: [ - [11, 22], - [33, 44], - [55, 66], - [77, 88], - ], - }, - { - input: [ - [ - [1, 2, 3], - [4, 5, 6], + }, + { + input: [ + [ + [1, 2, 3], + [4, 5, 6], + ], + [ + [10, 20, 30], + [40, 50, 60], + ], ], - [ - [10, 20, 30], - [40, 50, 60], + expected: [ + [11, 22, 33], + [44, 55, 66], ], - ], - expected: [ - [11, 22, 33], - [44, 55, 66], - ], - }, - { - input: [ - [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], + }, + { + input: [ + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ], + [ + [10, 20, 30], + [40, 50, 60], + [70, 80, 90], + ], ], - [ - [10, 20, 30], - [40, 50, 60], - [70, 80, 90], + expected: [ + [11, 22, 33], + [44, 55, 66], + [77, 88, 99], ], - ], - expected: [ - [11, 22, 33], - [44, 55, 66], - [77, 88, 99], - ], - }, - { - input: [ - [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10, 11, 12], + }, + { + input: [ + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10, 11, 12], + ], + [ + [10, 20, 30], + [40, 50, 60], + [70, 80, 90], + [1000, 1100, 1200], + ], ], - [ - [10, 20, 30], - [40, 50, 60], - [70, 80, 90], - [1000, 1100, 1200], + expected: [ + [11, 22, 33], + [44, 55, 66], + [77, 88, 99], + [1010, 1111, 1212], ], - ], - expected: [ - [11, 22, 33], - [44, 55, 66], - [77, 88, 99], - [1010, 1111, 1212], - ], - }, - { - input: [ - [ - [1, 2, 3, 4], - [5, 6, 7, 8], + }, + { + input: [ + [ + [1, 2, 3, 4], + [5, 6, 7, 8], + ], + [ + [10, 20, 30, 40], + [50, 60, 70, 80], + ], ], - [ - [10, 20, 30, 40], - [50, 60, 70, 80], + expected: [ + [11, 22, 33, 44], + [55, 66, 77, 88], ], - ], - expected: [ - [11, 22, 33, 44], - [55, 66, 77, 88], - ], - }, - { - input: [ - [ - [1, 2, 3, 4], - [5, 6, 7, 8], - [9, 10, 11, 12], + }, + { + input: [ + [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + ], + [ + [10, 20, 30, 40], + [50, 60, 70, 80], + [90, 1000, 1100, 1200], + ], ], - [ - [10, 20, 30, 40], - [50, 60, 70, 80], - [90, 1000, 1100, 1200], + expected: [ + [11, 22, 33, 44], + [55, 66, 77, 88], + [99, 1010, 1111, 1212], ], - ], - expected: [ - [11, 22, 33, 44], - [55, 66, 77, 88], - [99, 1010, 1111, 1212], - ], - }, - { - input: [ - [ - [1, 2, 3, 4], - [5, 6, 7, 8], - [9, 10, 11, 12], - [13, 14, 15, 16], + }, + { + input: [ + [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + [13, 14, 15, 16], + ], + [ + [10, 20, 30, 40], + [50, 60, 70, 80], + [90, 1000, 1100, 1200], + [1300, 1400, 1500, 1600], + ], ], - [ - [10, 20, 30, 40], - [50, 60, 70, 80], - [90, 1000, 1100, 1200], - [1300, 1400, 1500, 1600], + expected: [ + [11, 22, 33, 44], + [55, 66, 77, 88], + [99, 1010, 1111, 1212], + [1313, 1414, 1515, 1616], ], - ], - expected: [ - [11, 22, 33, 44], - [55, 66, 77, 88], - [99, 1010, 1111, 1212], - [1313, 1414, 1515, 1616], - ], - }, - ]) + }, + // Test the OOB is handled component-wise + { + input: [ + [ + [constants.positive.max, 2], + [3, 4], + ], + [ + [constants.positive.max, 20], + [30, 40], + ], + ], + expected: [ + [kUnboundedEndpoints, 22], + [33, 44], + ], + }, + ]; + }) ) .fn(t => { const [x, y] = t.params.input; @@ -6839,184 +6913,205 @@ g.test('subtractionMatrixMatrixInterval') u .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() - .combineWithParams<MatrixPairToMatrixCase>([ - // Only testing that different shapes of matrices are handled correctly - // here, to reduce test duplication. - // subtractionMatrixMatrixInterval uses AdditionIntervalOp for calculating intervals, - // so the testing for subtractionInterval covers the actual interval - // calculations. - { - input: [ - [ - [1, 2], - [3, 4], + .expandWithParams<MatrixPairToMatrixCase>(p => { + const trait = FP[p.trait]; + const constants = trait.constants(); + return [ + // Only testing that different shapes of matrices are handled correctly + // here, to reduce test duplication. + // subtractionMatrixMatrixInterval uses AdditionIntervalOp for calculating intervals, + // so the testing for subtractionInterval covers the actual interval + // calculations. + { + input: [ + [ + [1, 2], + [3, 4], + ], + [ + [-10, -20], + [-30, -40], + ], ], - [ - [-10, -20], - [-30, -40], + expected: [ + [11, 22], + [33, 44], ], - ], - expected: [ - [11, 22], - [33, 44], - ], - }, - { - input: [ - [ - [1, 2], - [3, 4], - [5, 6], + }, + { + input: [ + [ + [1, 2], + [3, 4], + [5, 6], + ], + [ + [-10, -20], + [-30, -40], + [-50, -60], + ], ], - [ - [-10, -20], - [-30, -40], - [-50, -60], + expected: [ + [11, 22], + [33, 44], + [55, 66], ], - ], - expected: [ - [11, 22], - [33, 44], - [55, 66], - ], - }, - { - input: [ - [ - [1, 2], - [3, 4], - [5, 6], - [7, 8], + }, + { + input: [ + [ + [1, 2], + [3, 4], + [5, 6], + [7, 8], + ], + [ + [-10, -20], + [-30, -40], + [-50, -60], + [-70, -80], + ], ], - [ - [-10, -20], - [-30, -40], - [-50, -60], - [-70, -80], + expected: [ + [11, 22], + [33, 44], + [55, 66], + [77, 88], ], - ], - expected: [ - [11, 22], - [33, 44], - [55, 66], - [77, 88], - ], - }, - { - input: [ - [ - [1, 2, 3], - [4, 5, 6], + }, + { + input: [ + [ + [1, 2, 3], + [4, 5, 6], + ], + [ + [-10, -20, -30], + [-40, -50, -60], + ], ], - [ - [-10, -20, -30], - [-40, -50, -60], + expected: [ + [11, 22, 33], + [44, 55, 66], ], - ], - expected: [ - [11, 22, 33], - [44, 55, 66], - ], - }, - { - input: [ - [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], + }, + { + input: [ + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ], + [ + [-10, -20, -30], + [-40, -50, -60], + [-70, -80, -90], + ], ], - [ - [-10, -20, -30], - [-40, -50, -60], - [-70, -80, -90], + expected: [ + [11, 22, 33], + [44, 55, 66], + [77, 88, 99], ], - ], - expected: [ - [11, 22, 33], - [44, 55, 66], - [77, 88, 99], - ], - }, - { - input: [ - [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10, 11, 12], + }, + { + input: [ + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10, 11, 12], + ], + [ + [-10, -20, -30], + [-40, -50, -60], + [-70, -80, -90], + [-1000, -1100, -1200], + ], ], - [ - [-10, -20, -30], - [-40, -50, -60], - [-70, -80, -90], - [-1000, -1100, -1200], + expected: [ + [11, 22, 33], + [44, 55, 66], + [77, 88, 99], + [1010, 1111, 1212], ], - ], - expected: [ - [11, 22, 33], - [44, 55, 66], - [77, 88, 99], - [1010, 1111, 1212], - ], - }, - { - input: [ - [ - [1, 2, 3, 4], - [5, 6, 7, 8], + }, + { + input: [ + [ + [1, 2, 3, 4], + [5, 6, 7, 8], + ], + [ + [-10, -20, -30, -40], + [-50, -60, -70, -80], + ], ], - [ - [-10, -20, -30, -40], - [-50, -60, -70, -80], + expected: [ + [11, 22, 33, 44], + [55, 66, 77, 88], ], - ], - expected: [ - [11, 22, 33, 44], - [55, 66, 77, 88], - ], - }, - { - input: [ - [ - [1, 2, 3, 4], - [5, 6, 7, 8], - [9, 10, 11, 12], + }, + { + input: [ + [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + ], + [ + [-10, -20, -30, -40], + [-50, -60, -70, -80], + [-90, -1000, -1100, -1200], + ], ], - [ - [-10, -20, -30, -40], - [-50, -60, -70, -80], - [-90, -1000, -1100, -1200], + expected: [ + [11, 22, 33, 44], + [55, 66, 77, 88], + [99, 1010, 1111, 1212], ], - ], - expected: [ - [11, 22, 33, 44], - [55, 66, 77, 88], - [99, 1010, 1111, 1212], - ], - }, - { - input: [ - [ - [1, 2, 3, 4], - [5, 6, 7, 8], - [9, 10, 11, 12], - [13, 14, 15, 16], + }, + { + input: [ + [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + [13, 14, 15, 16], + ], + [ + [-10, -20, -30, -40], + [-50, -60, -70, -80], + [-90, -1000, -1100, -1200], + [-1300, -1400, -1500, -1600], + ], ], - [ - [-10, -20, -30, -40], - [-50, -60, -70, -80], - [-90, -1000, -1100, -1200], - [-1300, -1400, -1500, -1600], + expected: [ + [11, 22, 33, 44], + [55, 66, 77, 88], + [99, 1010, 1111, 1212], + [1313, 1414, 1515, 1616], ], - ], - expected: [ - [11, 22, 33, 44], - [55, 66, 77, 88], - [99, 1010, 1111, 1212], - [1313, 1414, 1515, 1616], - ], - }, - ]) + }, + // Test the OOB is handled component-wise + { + input: [ + [ + [constants.positive.max, 2], + [3, 4], + ], + [ + [constants.negative.min, -20], + [-30, -40], + ], + ], + expected: [ + [kUnboundedEndpoints, 22], + [33, 44], + ], + }, + ]; + }) ) .fn(t => { const [x, y] = t.params.input; @@ -7577,7 +7672,7 @@ g.test('multiplicationMatrixMatrixInterval') interface MatrixScalarToMatrixCase { matrix: number[][]; scalar: number; - expected: (number | IntervalBounds)[][]; + expected: (number | IntervalEndpoints)[][]; } const kMultiplicationMatrixScalarIntervalCases = { @@ -7609,14 +7704,30 @@ const kMultiplicationMatrixScalarIntervalCases = { ], }, ] as MatrixScalarToMatrixCase[], + abstract: [ + // From https://github.com/gpuweb/cts/issues/3044 + { + matrix: [ + [kValue.f64.negative.min, 0], + [0, 0], + ], + scalar: kValue.f64.negative.subnormal.min, + expected: [ + [[0, reinterpretU64AsF64(0x400ffffffffffffdn)], 0], // [[0, 3.9999995...], 0], + [0, 0], + ], + }, + ] as MatrixScalarToMatrixCase[], } as const; g.test('multiplicationMatrixScalarInterval') .params(u => u - .combine('trait', ['f32', 'f16'] as const) + .combine('trait', ['f32', 'f16', 'abstract'] as const) .beginSubcases() .expandWithParams<MatrixScalarToMatrixCase>(p => { + const trait = FP[p.trait]; + const constants = trait.constants(); // Primarily testing that different shapes of matrices are handled correctly // here, to reduce test duplication. Additional testing for edge case // discovered in https://github.com/gpuweb/cts/issues/3044. @@ -7743,6 +7854,18 @@ g.test('multiplicationMatrixScalarInterval') ], }, ...kMultiplicationMatrixScalarIntervalCases[p.trait], + // Test that OOB is component-wise + { + matrix: [ + [1, 2], + [constants.positive.max, 4], + ], + scalar: 10, + expected: [ + [10, 20], + [kUnboundedEndpoints, 40], + ], + }, ]; }) ) @@ -7766,7 +7889,7 @@ g.test('multiplicationMatrixScalarInterval') interface MatrixVectorToVectorCase { matrix: number[][]; vector: number[]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } g.test('multiplicationMatrixVectorInterval') @@ -7883,7 +8006,7 @@ g.test('multiplicationMatrixVectorInterval') interface VectorMatrixToVectorCase { vector: number[]; matrix: number[][]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } g.test('multiplicationVectorMatrixInterval') @@ -7897,8 +8020,8 @@ g.test('multiplicationVectorMatrixInterval') // multiplicationVectorMatrixInterval uses DotIntervalOp for calculating // intervals, so the testing for dotInterval covers the actual interval // calculations. - // Keep all expected result integer no larger than 2047 to ensure that all result is exactly - // represeantable in both f32 and f16. + // Keep all expected result integer no larger than 2047 to ensure that + // all result is exactly representable in both f32 and f16. { vector: [1, 2], matrix: [ @@ -8002,7 +8125,7 @@ g.test('multiplicationVectorMatrixInterval') interface FaceForwardCase { input: [number[], number[], number[]]; - expected: ((number | IntervalBounds)[] | undefined)[]; + expected: ((number | IntervalEndpoints)[] | undefined)[]; } g.test('faceForwardIntervals') @@ -8081,8 +8204,8 @@ g.test('faceForwardIntervals') interface ModfCase { input: number; - fract: number | IntervalBounds; - whole: number | IntervalBounds; + fract: number | IntervalEndpoints; + whole: number | IntervalEndpoints; } g.test('modfInterval') @@ -8135,18 +8258,18 @@ g.test('modfInterval') interface RefractCase { input: [number[], number[], number]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } // Scope for refractInterval tests so that they can have constants for magic // numbers that don't pollute the global namespace or have unwieldy long names. { - const kNegativeOneBounds = { + const kNegativeOneEndpoints = { f32: [ reinterpretU64AsF64(0xbff0_0000_c000_0000n), reinterpretU64AsF64(0xbfef_ffff_4000_0000n), - ] as IntervalBounds, - f16: [reinterpretU16AsF16(0xbc06), reinterpretU16AsF16(0xbbfa)] as IntervalBounds, + ] as IntervalEndpoints, + f16: [reinterpretU16AsF16(0xbc06), reinterpretU16AsF16(0xbbfa)] as IntervalEndpoints, } as const; // prettier-ignore @@ -8178,7 +8301,7 @@ interface RefractCase { // vec4 // x = [1, -2, 3, -4], y = [-5, 6, -7, 8], z = 9, // dot(y, x) = -71, k = 1.0 - 9 * 9 * (1.0 - 71 * 71) = 408241 overflow f16. - { input: [[1, -2, 3, -4], [-5, 6, -7, 8], 9], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, + { input: [[1, -2, 3, -4], [-5, 6, -7, 8], 9], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, // x = [1, -2, 3, -4], y = [-5, 4, -3, 2], z = 2.5, // dot(y, x) = -30, k = 1.0 - 2.5 * 2.5 * (1.0 - 30 * 30) = 5619.75. // a = z * dot(y, x) + sqrt(k) = ~-0.035, result is about z * x - a * y = [~2.325, ~-4.86, ~7.4025, ~-9.93] @@ -8205,23 +8328,23 @@ interface RefractCase { { input: [[1, 1], [0.1, 0], 10], expected: [0, 0] }, // k contains 0 - { input: [[1, 1], [0.1, 0], 1.005038], expected: [kUnboundedBounds, kUnboundedBounds] }, + { input: [[1, 1], [0.1, 0], 1.005038], expected: [kUnboundedEndpoints, kUnboundedEndpoints] }, // k > 0 // vec2 - { input: [[1, 1], [1, 0], 1], expected: [kNegativeOneBounds[p.trait], 1] }, + { input: [[1, 1], [1, 0], 1], expected: [kNegativeOneEndpoints[p.trait], 1] }, // vec3 - { input: [[1, 1, 1], [1, 0, 0], 1], expected: [kNegativeOneBounds[p.trait], 1, 1] }, + { input: [[1, 1, 1], [1, 0, 0], 1], expected: [kNegativeOneEndpoints[p.trait], 1, 1] }, // vec4 - { input: [[1, 1, 1, 1], [1, 0, 0, 0], 1], expected: [kNegativeOneBounds[p.trait], 1, 1, 1] }, - - // Test that dot going OOB bounds in the intermediate calculations propagates - { input: [[constants.positive.nearest_max, constants.positive.max, constants.negative.min], [1.0, 1.0, 1.0], 1], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.positive.nearest_max, constants.negative.min, constants.positive.max], [1.0, 1.0, 1.0], 1], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.positive.max, constants.positive.nearest_max, constants.negative.min], [1.0, 1.0, 1.0], 1], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.negative.min, constants.positive.nearest_max, constants.positive.max], [1.0, 1.0, 1.0], 1], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.positive.max, constants.negative.min, constants.positive.nearest_max], [1.0, 1.0, 1.0], 1], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, - { input: [[constants.negative.min, constants.positive.max, constants.positive.nearest_max], [1.0, 1.0, 1.0], 1], expected: [kUnboundedBounds, kUnboundedBounds, kUnboundedBounds] }, + { input: [[1, 1, 1, 1], [1, 0, 0, 0], 1], expected: [kNegativeOneEndpoints[p.trait], 1, 1, 1] }, + + // Test that dot going OOB in the intermediate calculations propagates + { input: [[constants.positive.nearest_max, constants.positive.max, constants.negative.min], [1.0, 1.0, 1.0], 1], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.positive.nearest_max, constants.negative.min, constants.positive.max], [1.0, 1.0, 1.0], 1], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.positive.max, constants.positive.nearest_max, constants.negative.min], [1.0, 1.0, 1.0], 1], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.negative.min, constants.positive.nearest_max, constants.positive.max], [1.0, 1.0, 1.0], 1], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.positive.max, constants.negative.min, constants.positive.nearest_max], [1.0, 1.0, 1.0], 1], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, + { input: [[constants.negative.min, constants.positive.max, constants.positive.nearest_max], [1.0, 1.0, 1.0], 1], expected: [kUnboundedEndpoints, kUnboundedEndpoints, kUnboundedEndpoints] }, ]; }) ) diff --git a/dom/webgpu/tests/cts/checkout/src/unittests/maths.spec.ts b/dom/webgpu/tests/cts/checkout/src/unittests/maths.spec.ts index 357c574281..d84299d993 100644 --- a/dom/webgpu/tests/cts/checkout/src/unittests/maths.spec.ts +++ b/dom/webgpu/tests/cts/checkout/src/unittests/maths.spec.ts @@ -22,8 +22,8 @@ import { correctlyRoundedF32, FlushMode, frexp, - fullF16Range, - fullF32Range, + scalarF16Range, + scalarF32Range, fullI32Range, lerp, linearRange, @@ -36,6 +36,7 @@ import { oneULPF64, lerpBigInt, linearRangeBigInt, + biasedRangeBigInt, } from '../webgpu/util/math.js'; import { reinterpretU16AsF16, @@ -1525,6 +1526,41 @@ g.test('linearRangeBigInt') ); }); +g.test('biasedRangeBigInt') + .paramsSimple<rangeBigIntCase>( + // prettier-ignore + [ + { a: 0n, b: 0n, num_steps: 10, result: new Array<bigint>(10).fill(0n) }, + { a: 10n, b: 10n, num_steps: 10, result: new Array<bigint>(10).fill(10n) }, + { a: 0n, b: 10n, num_steps: 1, result: [0n] }, + { a: 10n, b: 0n, num_steps: 1, result: [10n] }, + { a: 0n, b: 10n, num_steps: 11, result: [0n, 0n, 0n, 0n, 1n, 2n, 3n, 4n, 6n, 8n, 10n] }, + { a: 10n, b: 0n, num_steps: 11, result: [10n, 10n, 10n, 10n, 9n, 8n, 7n, 6n, 4n, 2n, 0n] }, + { a: 0n, b: 1000n, num_steps: 11, result: [0n, 9n, 39n, 89n, 159n, 249n, 359n, 489n, 639n, 809n, 1000n] }, + { a: 1000n, b: 0n, num_steps: 11, result: [1000n, 991n, 961n, 911n, 841n, 751n, 641n, 511n, 361n, 191n, 0n] }, + { a: 1n, b: 5n, num_steps: 5, result: [1n, 1n, 2n, 3n, 5n] }, + { a: 5n, b: 1n, num_steps: 5, result: [5n, 5n, 4n, 3n, 1n] }, + { a: 0n, b: 10n, num_steps: 5, result: [0n, 0n, 2n, 5n, 10n] }, + { a: 10n, b: 0n, num_steps: 5, result: [10n, 10n, 8n, 5n, 0n] }, + { a: -10n, b: 10n, num_steps: 11, result: [-10n, -10n, -10n, -10n, -8n, -6n, -4n, -2n, 2n, 6n, 10n] }, + { a: 10n, b: -10n, num_steps: 11, result: [10n, 10n, 10n, 10n, 8n, 6n, 4n, 2n, -2n, -6n, -10n] }, + { a: -10n, b: 0n, num_steps: 11, result: [-10n, -10n, -10n, -10n, -9n, -8n, -7n, -6n, -4n, -2n, -0n] }, + { a: 0n, b: -10n, num_steps: 11, result: [0n, 0n, 0n, 0n, -1n, -2n, -3n, -4n, -6n, -8n, -10n] }, + ] + ) + .fn(test => { + const a = test.params.a; + const b = test.params.b; + const num_steps = test.params.num_steps; + const got = biasedRangeBigInt(a, b, num_steps); + const expect = test.params.result; + + test.expect( + objectEquals(got, expect), + `biasedRangeBigInt(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}` + ); + }); + interface fullF32RangeCase { neg_norm: number; neg_sub: number; @@ -1557,7 +1593,7 @@ g.test('fullF32Range') const neg_sub = test.params.neg_sub; const pos_sub = test.params.pos_sub; const pos_norm = test.params.pos_norm; - const got = fullF32Range({ neg_norm, neg_sub, pos_sub, pos_norm }); + const got = scalarF32Range({ neg_norm, neg_sub, pos_sub, pos_norm }); const expect = test.params.expect; test.expect( @@ -1598,7 +1634,7 @@ g.test('fullF16Range') const neg_sub = test.params.neg_sub; const pos_sub = test.params.pos_sub; const pos_norm = test.params.pos_norm; - const got = fullF16Range({ neg_norm, neg_sub, pos_sub, pos_norm }); + const got = scalarF16Range({ neg_norm, neg_sub, pos_sub, pos_norm }); const expect = test.params.expect; test.expect( diff --git a/dom/webgpu/tests/cts/checkout/src/unittests/parse_imports.spec.ts b/dom/webgpu/tests/cts/checkout/src/unittests/parse_imports.spec.ts new file mode 100644 index 0000000000..0efdc0d171 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/unittests/parse_imports.spec.ts @@ -0,0 +1,79 @@ +export const description = ` +Test for "parseImports" utility. +`; + +import { makeTestGroup } from '../common/framework/test_group.js'; +import { parseImports } from '../common/util/parse_imports.js'; + +import { UnitTest } from './unit_test.js'; + +class F extends UnitTest { + test(content: string, expect: string[]): void { + const got = parseImports('a/b/c.js', content); + const expectJoined = expect.join('\n'); + const gotJoined = got.join('\n'); + this.expect( + expectJoined === gotJoined, + ` +expected: ${expectJoined} +got: ${gotJoined}` + ); + } +} + +export const g = makeTestGroup(F); + +g.test('empty').fn(t => { + t.test(``, []); + t.test(`\n`, []); + t.test(`\n\n`, []); +}); + +g.test('simple').fn(t => { + t.test(`import 'x/y/z.js';`, ['a/b/x/y/z.js']); + t.test(`import * as blah from 'x/y/z.js';`, ['a/b/x/y/z.js']); + t.test(`import { blah } from 'x/y/z.js';`, ['a/b/x/y/z.js']); +}); + +g.test('multiple').fn(t => { + t.test( + ` +blah blah blah +import 'x/y/z.js'; +more blah +import * as blah from 'm/n/o.js'; +extra blah +import { blah } from '../h.js'; +ending with blah +`, + ['a/b/x/y/z.js', 'a/b/m/n/o.js', 'a/h.js'] + ); +}); + +g.test('multiline').fn(t => { + t.test( + `import { + blah +} from 'x/y/z.js';`, + ['a/b/x/y/z.js'] + ); + t.test( + `import { + blahA, + blahB, +} from 'x/y/z.js';`, + ['a/b/x/y/z.js'] + ); +}); + +g.test('file_characters').fn(t => { + t.test(`import '01234_56789.js';`, ['a/b/01234_56789.js']); +}); + +g.test('relative_paths').fn(t => { + t.test(`import '../x.js';`, ['a/x.js']); + t.test(`import '../x/y.js';`, ['a/x/y.js']); + t.test(`import '../../x.js';`, ['x.js']); + t.test(`import '../../../x.js';`, ['../x.js']); + t.test(`import '../../../../x.js';`, ['../../x.js']); +}); diff --git a/dom/webgpu/tests/cts/checkout/src/unittests/serialization.spec.ts b/dom/webgpu/tests/cts/checkout/src/unittests/serialization.spec.ts index 9717ba3ecf..ea6ed5e42f 100644 --- a/dom/webgpu/tests/cts/checkout/src/unittests/serialization.spec.ts +++ b/dom/webgpu/tests/cts/checkout/src/unittests/serialization.spec.ts @@ -16,6 +16,8 @@ import { } from '../webgpu/util/compare.js'; import { kValue } from '../webgpu/util/constants.js'; import { + abstractFloat, + abstractInt, bool, deserializeValue, f16, @@ -61,6 +63,18 @@ g.test('value').fn(t => { u8(kValue.u8.max - 1), u8(kValue.u8.max - 0), + abstractInt(kValue.i64.negative.min), + abstractInt(kValue.i64.negative.min + 1n), + abstractInt(kValue.i64.negative.min + 2n), + abstractInt(kValue.i64.negative.max - 2n), + abstractInt(kValue.i64.negative.max - 1n), + abstractInt(kValue.i64.positive.min), + abstractInt(kValue.i64.positive.min + 1n), + abstractInt(kValue.i64.positive.min + 2n), + abstractInt(kValue.i64.positive.max - 2n), + abstractInt(kValue.i64.positive.max - 1n), + abstractInt(kValue.i64.positive.max), + i32(kValue.i32.negative.min + 0), i32(kValue.i32.negative.min + 1), i32(kValue.i32.negative.min + 2), @@ -97,6 +111,21 @@ g.test('value').fn(t => { i8(kValue.i8.positive.max - 1), i8(kValue.i8.positive.max - 0), + abstractFloat(0), + abstractFloat(-0), + abstractFloat(1), + abstractFloat(-1), + abstractFloat(0.5), + abstractFloat(-0.5), + abstractFloat(kValue.f64.positive.max), + abstractFloat(kValue.f64.positive.min), + abstractFloat(kValue.f64.positive.subnormal.max), + abstractFloat(kValue.f64.positive.subnormal.min), + abstractFloat(kValue.f64.negative.subnormal.max), + abstractFloat(kValue.f64.negative.subnormal.min), + abstractFloat(kValue.f64.positive.infinity), + abstractFloat(kValue.f64.negative.infinity), + f32(0), f32(-0), f32(1), @@ -139,6 +168,13 @@ g.test('value').fn(t => { [0.0, 1.0], [2.0, 3.0], ], + abstractFloat + ), + toMatrix( + [ + [0.0, 1.0], + [2.0, 3.0], + ], f32 ), toMatrix( @@ -153,6 +189,13 @@ g.test('value').fn(t => { [0.0, 1.0, 2.0, 3.0], [4.0, 5.0, 6.0, 7.0], ], + abstractFloat + ), + toMatrix( + [ + [0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + ], f32 ), toMatrix( @@ -169,6 +212,14 @@ g.test('value').fn(t => { [3.0, 4.0, 5.0], [6.0, 7.0, 8.0], ], + abstractFloat + ), + toMatrix( + [ + [0.0, 1.0, 2.0], + [3.0, 4.0, 5.0], + [6.0, 7.0, 8.0], + ], f32 ), toMatrix( @@ -186,6 +237,15 @@ g.test('value').fn(t => { [4.0, 5.0], [6.0, 7.0], ], + abstractFloat + ), + toMatrix( + [ + [0.0, 1.0], + [2.0, 3.0], + [4.0, 5.0], + [6.0, 7.0], + ], f32 ), toMatrix( @@ -204,6 +264,15 @@ g.test('value').fn(t => { [8.0, 9.0, 10.0, 11.0], [12.0, 13.0, 14.0, 15.0], ], + abstractFloat + ), + toMatrix( + [ + [0.0, 1.0, 2.0, 3.0], + [4.0, 5.0, 6.0, 7.0], + [8.0, 9.0, 10.0, 11.0], + [12.0, 13.0, 14.0, 15.0], + ], f32 ), ]) { diff --git a/dom/webgpu/tests/cts/checkout/src/unittests/texture_ok.spec.ts b/dom/webgpu/tests/cts/checkout/src/unittests/texture_ok.spec.ts index f1e6971a74..c126832b8d 100644 --- a/dom/webgpu/tests/cts/checkout/src/unittests/texture_ok.spec.ts +++ b/dom/webgpu/tests/cts/checkout/src/unittests/texture_ok.spec.ts @@ -4,7 +4,6 @@ Test for texture_ok utils. import { makeTestGroup } from '../common/framework/test_group.js'; import { typedArrayFromParam, typedArrayParam } from '../common/util/util.js'; -import { RegularTextureFormat } from '../webgpu/format_info.js'; import { TexelView } from '../webgpu/util/texture/texel_view.js'; import { findFailedPixels } from '../webgpu/util/texture/texture_ok.js'; @@ -30,103 +29,103 @@ g.test('findFailedPixels') u.combineWithParams([ // Sanity Check { - format: 'rgba8unorm' as RegularTextureFormat, + format: 'rgba8unorm', actual: typedArrayParam('Uint8Array', [0x00, 0x40, 0x80, 0xff]), expected: typedArrayParam('Uint8Array', [0x00, 0x40, 0x80, 0xff]), isSame: true, }, // Slightly different values { - format: 'rgba8unorm' as RegularTextureFormat, + format: 'rgba8unorm', actual: typedArrayParam('Uint8Array', [0x00, 0x40, 0x80, 0xff]), expected: typedArrayParam('Uint8Array', [0x00, 0x40, 0x81, 0xff]), isSame: false, }, // Different representations of the same value { - format: 'rgb9e5ufloat' as RegularTextureFormat, + format: 'rgb9e5ufloat', actual: typedArrayParam('Uint8Array', [0x78, 0x56, 0x34, 0x12]), expected: typedArrayParam('Uint8Array', [0xf0, 0xac, 0x68, 0x0c]), isSame: true, }, // Slightly different values { - format: 'rgb9e5ufloat' as RegularTextureFormat, + format: 'rgb9e5ufloat', actual: typedArrayParam('Uint8Array', [0x78, 0x56, 0x34, 0x12]), expected: typedArrayParam('Uint8Array', [0xf1, 0xac, 0x68, 0x0c]), isSame: false, }, // Test NaN === NaN { - format: 'r32float' as RegularTextureFormat, + format: 'r32float', actual: typedArrayParam('Float32Array', [parseFloat('abc')]), expected: typedArrayParam('Float32Array', [parseFloat('def')]), isSame: true, }, // Sanity Check { - format: 'r32float' as RegularTextureFormat, + format: 'r32float', actual: typedArrayParam('Float32Array', [1.23]), expected: typedArrayParam('Float32Array', [1.23]), isSame: true, }, // Slightly different values. { - format: 'r32float' as RegularTextureFormat, + format: 'r32float', actual: typedArrayParam('Uint32Array', [0x3f9d70a4]), expected: typedArrayParam('Uint32Array', [0x3f9d70a5]), isSame: false, }, // Slightly different { - format: 'rg11b10ufloat' as RegularTextureFormat, + format: 'rg11b10ufloat', actual: typedArrayParam('Uint32Array', [0x3ce]), expected: typedArrayParam('Uint32Array', [0x3cf]), isSame: false, }, // Positive.Infinity === Positive.Infinity (red) { - format: 'rg11b10ufloat' as RegularTextureFormat, + format: 'rg11b10ufloat', actual: typedArrayParam('Uint32Array', [0b11111000000]), expected: typedArrayParam('Uint32Array', [0b11111000000]), isSame: true, }, // Positive.Infinity === Positive.Infinity (green) { - format: 'rg11b10ufloat' as RegularTextureFormat, + format: 'rg11b10ufloat', actual: typedArrayParam('Uint32Array', [0b11111000000_00000000000]), expected: typedArrayParam('Uint32Array', [0b11111000000_00000000000]), isSame: true, }, // Positive.Infinity === Positive.Infinity (blue) { - format: 'rg11b10ufloat' as RegularTextureFormat, + format: 'rg11b10ufloat', actual: typedArrayParam('Uint32Array', [0b1111100000_00000000000_00000000000]), expected: typedArrayParam('Uint32Array', [0b1111100000_00000000000_00000000000]), isSame: true, }, // NaN === NaN (red) { - format: 'rg11b10ufloat' as RegularTextureFormat, + format: 'rg11b10ufloat', actual: typedArrayParam('Uint32Array', [0b11111000001]), expected: typedArrayParam('Uint32Array', [0b11111000010]), isSame: true, }, // NaN === NaN (green) { - format: 'rg11b10ufloat' as RegularTextureFormat, + format: 'rg11b10ufloat', actual: typedArrayParam('Uint32Array', [0b11111000100_00000000000]), expected: typedArrayParam('Uint32Array', [0b11111001000_00000000000]), isSame: true, }, // NaN === NaN (blue) { - format: 'rg11b10ufloat' as RegularTextureFormat, + format: 'rg11b10ufloat', actual: typedArrayParam('Uint32Array', [0b1111110000_00000000000_00000000000]), expected: typedArrayParam('Uint32Array', [0b1111101000_00000000000_00000000000]), isSame: true, }, - ]) + ] as const) ) .fn(t => { const { format, actual, expected, isSame } = t.params; |