diff --git a/docs/fp_primer.md b/docs/fp_primer.md index a8302fb46172..5d816a7e5b4a 100644 --- a/docs/fp_primer.md +++ b/docs/fp_primer.md @@ -39,7 +39,8 @@ A floating point number system defines - Arithmetic operations on those representatives, trying to approximate the ideal operations on real numbers. -The cardinality mismatch alone implies that any floating point number system necessarily loses information. +The cardinality mismatch alone implies that any floating point number system +necessarily loses information. This means that not all numbers in the bounds can be exactly represented as a floating point value. @@ -114,7 +115,7 @@ Implementations may assume that infinities are not present. When an evaluation at runtime would produce an infinity, an indeterminate value is produced instead. -When a value goes out of bounds for a specific precision there are special +When a value goes out-of-bounds for a specific precision there are special rounding rules that apply. If it is 'near' the edge of finite values for that precision, it is considered to be near-overflowing, and the implementation may choose to round it to the edge value or the appropriate infinity. If it is not @@ -163,7 +164,7 @@ the rules for compile time execution will be discussed below.) Signaling NaNs are treated as quiet NaNs in the WGSL spec. And quiet NaNs have the same "may-convert-to-indeterminate-value" behaviour that infinities have, so -for the purpose of the CTS they are handled by the infinite/out of bounds logic +for the purpose of the CTS they are handled by the infinite/out-of-bounds logic normally. ## Notation/Terminology @@ -231,14 +232,20 @@ referred to as the beginning of the interval and `b` as the end of the interval. When talking about intervals, this doc and the code endeavours to avoid using the term **range** to refer to the span of values that an interval covers, -instead using the term bounds to avoid confusion of terminology around output of -operations. +instead using the term **endpoints** to avoid confusion of terminology around +output of operations. + +The term **endpoints** is generally used to refer to the conceptual numeric +spaces, i.e. f32 or abstract float. + +Thus a specific interval can have **endpoints** that are either in or out of +bounds for a specific floating point precision. ## Accuracy As mentioned above floating point numbers are not able to represent all the -possible values over their bounds, but instead represent discrete values in that -interval, and approximate the remainder. +possible values over their range, but instead represent discrete values in that +space, and approximate the remainder. Additionally, floating point numbers are not evenly distributed over the real number line, but instead are more densely clustered around zero, with the space @@ -398,7 +405,7 @@ That would be very inefficient though and make your reviewer sad to read. For mapping intervals to intervals the key insight is that we only need to be concerned with the extrema of the operation in the interval, since the -acceptance interval is the bounds of the possible outputs. +acceptance interval is defined by largest and smallest of the possible outputs. In more precise terms: ``` @@ -652,7 +659,7 @@ the theoretical world that the intervals being used for testing are infinitely precise, when in actuality they are implemented by the ECMAScript `number` type, which is implemented as a f64 value. -For the vast majority of cases, even out of bounds and overflow, this is +For the vast majority of cases, even out-of-bounds and overflow, this is sufficient. There is one small slice where this breaks down. Specifically if the result just outside the finite range by less than 1 f64 ULP of the edge value. An example of this is `2 ** -11 + f32.max`. This will be between `f32.max` @@ -752,7 +759,7 @@ const_assert upper > foo(x) // Result was above the acceptance interval ``` where lower and upper would actually be string replaced with literals for the -bounds of the acceptance interval when generating the shader text. +endpoints of the acceptance interval when generating the shader text. This approach has a number of limitations that made it unacceptable for the CTS. First, how errors are reported is a pain to debug. Someone working with the CTS diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 171297b815c2..80b8cd524cc5 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,103 +1,103 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "b5345cca", + "webgpu/shader/execution/binary/af_addition.bin": "a9c5cce6", "webgpu/shader/execution/binary/af_logical.bin": "9ec85311", - "webgpu/shader/execution/binary/af_division.bin": "23dd840e", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "ee95539e", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "a2e64d57", - "webgpu/shader/execution/binary/af_multiplication.bin": "94d7a574", - "webgpu/shader/execution/binary/af_remainder.bin": "79f5abc4", - "webgpu/shader/execution/binary/af_subtraction.bin": "62dce5bf", - "webgpu/shader/execution/binary/f16_addition.bin": "ab3b4d9c", + "webgpu/shader/execution/binary/af_division.bin": "8d23c845", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "63a6a1ac", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "120cfedf", + "webgpu/shader/execution/binary/af_multiplication.bin": "94eed575", + "webgpu/shader/execution/binary/af_remainder.bin": "7d4767a5", + "webgpu/shader/execution/binary/af_subtraction.bin": "5a18e960", + "webgpu/shader/execution/binary/f16_addition.bin": "7a67e7b1", "webgpu/shader/execution/binary/f16_logical.bin": "1edd08ec", - "webgpu/shader/execution/binary/f16_division.bin": "849eed26", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "54afc3f8", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "df5d3ccc", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "3be6032e", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "98a29837", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "c463d7e0", - "webgpu/shader/execution/binary/f16_multiplication.bin": "cdf0775d", - "webgpu/shader/execution/binary/f16_remainder.bin": "e82ef89f", - "webgpu/shader/execution/binary/f16_subtraction.bin": "5125b5b0", - "webgpu/shader/execution/binary/f32_addition.bin": "d3b8004", + "webgpu/shader/execution/binary/f16_division.bin": "127a1260", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "f3a630df", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "e396f4b8", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "bfbed0d7", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "50d9bac9", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "9b0ddaa2", + "webgpu/shader/execution/binary/f16_multiplication.bin": "6b29214d", + "webgpu/shader/execution/binary/f16_remainder.bin": "c45c8326", + "webgpu/shader/execution/binary/f16_subtraction.bin": "44437355", + "webgpu/shader/execution/binary/f32_addition.bin": "ef227af", "webgpu/shader/execution/binary/f32_logical.bin": "fab7cfc5", - "webgpu/shader/execution/binary/f32_division.bin": "e9fdb0a9", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "d1165469", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "45a79521", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "bf8da7d0", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "82d7fa9", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "57eebe84", - "webgpu/shader/execution/binary/f32_multiplication.bin": "eb0baa56", - "webgpu/shader/execution/binary/f32_remainder.bin": "e6059990", - "webgpu/shader/execution/binary/f32_subtraction.bin": "d57cfa1", + "webgpu/shader/execution/binary/f32_division.bin": "c2df73c9", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "18502449", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "96cda4b2", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "5570ca6f", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "a0d7e8d1", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "90b6aeb9", + "webgpu/shader/execution/binary/f32_multiplication.bin": "f6dff4c7", + "webgpu/shader/execution/binary/f32_remainder.bin": "1d5f3a8", + "webgpu/shader/execution/binary/f32_subtraction.bin": "b9a951e9", "webgpu/shader/execution/binary/i32_arithmetic.bin": "de93ee2a", "webgpu/shader/execution/binary/i32_comparison.bin": "aaa1f21b", "webgpu/shader/execution/binary/u32_arithmetic.bin": "d79a1011", "webgpu/shader/execution/binary/u32_comparison.bin": "31764c75", - "webgpu/shader/execution/abs.bin": "9278901c", - "webgpu/shader/execution/acos.bin": "9c77ebf9", - "webgpu/shader/execution/acosh.bin": "2baaf7cc", - "webgpu/shader/execution/asin.bin": "8fd7a678", - "webgpu/shader/execution/asinh.bin": "321a7294", - "webgpu/shader/execution/atan.bin": "d3dada7e", - "webgpu/shader/execution/atan2.bin": "2871596c", - "webgpu/shader/execution/atanh.bin": "e7158ef", - "webgpu/shader/execution/bitcast.bin": "aa7d6f0e", - "webgpu/shader/execution/ceil.bin": "3c1d91ad", - "webgpu/shader/execution/clamp.bin": "a53fdbda", - "webgpu/shader/execution/cos.bin": "73b83288", - "webgpu/shader/execution/cosh.bin": "39ca32a4", - "webgpu/shader/execution/cross.bin": "409c3e26", - "webgpu/shader/execution/degrees.bin": "a44bb2b1", - "webgpu/shader/execution/determinant.bin": "41a06528", - "webgpu/shader/execution/distance.bin": "fe3ea84f", - "webgpu/shader/execution/dot.bin": "f70abfec", - "webgpu/shader/execution/exp.bin": "7587e11b", - "webgpu/shader/execution/exp2.bin": "c6f34d2b", - "webgpu/shader/execution/faceForward.bin": "8eb57a7d", - "webgpu/shader/execution/floor.bin": "b9ea647b", - "webgpu/shader/execution/fma.bin": "b9f657b2", - "webgpu/shader/execution/fract.bin": "73a18e4f", - "webgpu/shader/execution/frexp.bin": "857fe9b7", - "webgpu/shader/execution/inverseSqrt.bin": "a595983a", - "webgpu/shader/execution/ldexp.bin": "a04aaeca", - "webgpu/shader/execution/length.bin": "695740fb", - "webgpu/shader/execution/log.bin": "6703ec1a", - "webgpu/shader/execution/log2.bin": "8339559d", - "webgpu/shader/execution/max.bin": "ec000a56", - "webgpu/shader/execution/min.bin": "156cf7cc", - "webgpu/shader/execution/mix.bin": "32064d21", - "webgpu/shader/execution/modf.bin": "a5003ce0", - "webgpu/shader/execution/normalize.bin": "c12bac96", + "webgpu/shader/execution/abs.bin": "9e12d146", + "webgpu/shader/execution/acos.bin": "d72227bc", + "webgpu/shader/execution/acosh.bin": "b3008340", + "webgpu/shader/execution/asin.bin": "2348666b", + "webgpu/shader/execution/asinh.bin": "1fcc0429", + "webgpu/shader/execution/atan.bin": "34f29ec8", + "webgpu/shader/execution/atan2.bin": "33c3196c", + "webgpu/shader/execution/atanh.bin": "79cffc5a", + "webgpu/shader/execution/bitcast.bin": "c6e7405c", + "webgpu/shader/execution/ceil.bin": "4cabf37c", + "webgpu/shader/execution/clamp.bin": "8b4c84c7", + "webgpu/shader/execution/cos.bin": "dd4f0d54", + "webgpu/shader/execution/cosh.bin": "cf2303ea", + "webgpu/shader/execution/cross.bin": "f231a725", + "webgpu/shader/execution/degrees.bin": "216634ac", + "webgpu/shader/execution/determinant.bin": "3a4c5761", + "webgpu/shader/execution/distance.bin": "45c1bcee", + "webgpu/shader/execution/dot.bin": "553f268b", + "webgpu/shader/execution/exp.bin": "13f273d5", + "webgpu/shader/execution/exp2.bin": "215379e7", + "webgpu/shader/execution/faceForward.bin": "dfcee405", + "webgpu/shader/execution/floor.bin": "422b808a", + "webgpu/shader/execution/fma.bin": "df385cdd", + "webgpu/shader/execution/fract.bin": "416f144f", + "webgpu/shader/execution/frexp.bin": "48323de7", + "webgpu/shader/execution/inverseSqrt.bin": "b5d88ed0", + "webgpu/shader/execution/ldexp.bin": "82257924", + "webgpu/shader/execution/length.bin": "eef942e5", + "webgpu/shader/execution/log.bin": "6889843e", + "webgpu/shader/execution/log2.bin": "97bae6ee", + "webgpu/shader/execution/max.bin": "9369c070", + "webgpu/shader/execution/min.bin": "85cd40d4", + "webgpu/shader/execution/mix.bin": "f24ecba4", + "webgpu/shader/execution/modf.bin": "6914406e", + "webgpu/shader/execution/normalize.bin": "56db74b3", "webgpu/shader/execution/pack2x16float.bin": "b2cb12ea", - "webgpu/shader/execution/pow.bin": "ee87eccb", - "webgpu/shader/execution/quantizeToF16.bin": "f77ae7e3", - "webgpu/shader/execution/radians.bin": "7ecbe5be", - "webgpu/shader/execution/reflect.bin": "d37034bd", - "webgpu/shader/execution/refract.bin": "6dc9adcf", - "webgpu/shader/execution/round.bin": "d91faa0f", - "webgpu/shader/execution/saturate.bin": "93230980", - "webgpu/shader/execution/sign.bin": "fb1071b8", - "webgpu/shader/execution/sin.bin": "a9ed8361", - "webgpu/shader/execution/sinh.bin": "65ba80fc", - "webgpu/shader/execution/smoothstep.bin": "17e1e103", - "webgpu/shader/execution/sqrt.bin": "4c29a5d4", - "webgpu/shader/execution/step.bin": "aed08458", - "webgpu/shader/execution/tan.bin": "5f671594", - "webgpu/shader/execution/tanh.bin": "991e903a", - "webgpu/shader/execution/transpose.bin": "9d388797", - "webgpu/shader/execution/trunc.bin": "959fe8bc", - "webgpu/shader/execution/unpack2x16float.bin": "26e5b05e", - "webgpu/shader/execution/unpack2x16snorm.bin": "c756d8a3", - "webgpu/shader/execution/unpack2x16unorm.bin": "5257591a", - "webgpu/shader/execution/unpack4x8snorm.bin": "83fbd41a", - "webgpu/shader/execution/unpack4x8unorm.bin": "77d46acb", - "webgpu/shader/execution/unary/af_arithmetic.bin": "2edb2dc5", - "webgpu/shader/execution/unary/af_assignment.bin": "36c04bba", + "webgpu/shader/execution/pow.bin": "4559fe54", + "webgpu/shader/execution/quantizeToF16.bin": "6e7140c0", + "webgpu/shader/execution/radians.bin": "3d20d9af", + "webgpu/shader/execution/reflect.bin": "83096c2a", + "webgpu/shader/execution/refract.bin": "5ff885dc", + "webgpu/shader/execution/round.bin": "f8bdb72b", + "webgpu/shader/execution/saturate.bin": "fd71272f", + "webgpu/shader/execution/sign.bin": "5ac3fcf8", + "webgpu/shader/execution/sin.bin": "8eb2a6be", + "webgpu/shader/execution/sinh.bin": "33fffc1d", + "webgpu/shader/execution/smoothstep.bin": "33df4b5f", + "webgpu/shader/execution/sqrt.bin": "f0e8fd1", + "webgpu/shader/execution/step.bin": "eef87c59", + "webgpu/shader/execution/tan.bin": "4904b8c0", + "webgpu/shader/execution/tanh.bin": "b1afeda", + "webgpu/shader/execution/transpose.bin": "697e34a5", + "webgpu/shader/execution/trunc.bin": "4474ec0", + "webgpu/shader/execution/unpack2x16float.bin": "be34edc2", + "webgpu/shader/execution/unpack2x16snorm.bin": "68115c4", + "webgpu/shader/execution/unpack2x16unorm.bin": "b0a3bc8d", + "webgpu/shader/execution/unpack4x8snorm.bin": "778671b5", + "webgpu/shader/execution/unpack4x8unorm.bin": "686ea0d", + "webgpu/shader/execution/unary/af_arithmetic.bin": "b1492312", + "webgpu/shader/execution/unary/af_assignment.bin": "86ca08b5", "webgpu/shader/execution/unary/bool_conversion.bin": "cb53bf65", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "9f459fc4", - "webgpu/shader/execution/unary/f16_conversion.bin": "bf055ca9", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "e15c49e7", - "webgpu/shader/execution/unary/f32_conversion.bin": "92e5069f", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "69914092", + "webgpu/shader/execution/unary/f16_conversion.bin": "8a3ffbbb", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "61e25fb6", + "webgpu/shader/execution/unary/f32_conversion.bin": "c47c9b05", "webgpu/shader/execution/unary/i32_arithmetic.bin": "d322b73d", "webgpu/shader/execution/unary/i32_complement.bin": "c4e6cbb", "webgpu/shader/execution/unary/i32_conversion.bin": "d6905a0f", diff --git a/src/unittests/floating_point.spec.ts b/src/unittests/floating_point.spec.ts index 0d843076d7f3..f4aaf3e78841 100644 --- a/src/unittests/floating_point.spec.ts +++ b/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, @@ -27,14 +32,14 @@ const kFPTraitForULP = { 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 +94,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 +112,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 +133,8 @@ function applyError( // FPInterval interface ConstructorCase { - input: IntervalBounds; - expected: IntervalBounds; + input: IntervalEndpoints; + expected: IntervalEndpoints; } g.test('constructor') @@ -160,7 +165,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 +187,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 +208,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 +301,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 +323,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 +332,8 @@ g.test('contains_number') }); interface ContainsIntervalCase { - lhs: number | IntervalBounds; - rhs: number | IntervalBounds; + lhs: number | IntervalEndpoints; + rhs: number | IntervalEndpoints; expected: boolean; } @@ -440,8 +445,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 +472,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 +499,7 @@ g.test('spanIntervals') }); interface isVectorCase { - input: (number | IntervalBounds | FPIntervalParam)[]; + input: (number | IntervalEndpoints | FPIntervalParam)[]; expected: boolean; } @@ -511,7 +516,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 +605,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 +622,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 +709,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 +727,7 @@ g.test('toVector') }); interface isMatrixCase { - input: (number | IntervalBounds | FPIntervalParam)[][]; + input: (number | IntervalEndpoints | FPIntervalParam)[][]; expected: boolean; } @@ -808,7 +813,7 @@ g.test('isMatrix') expected: false, }, - // IntervalBounds + // IntervalEndpoints { input: [ [[1], [2]], @@ -1157,8 +1162,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 +1284,7 @@ g.test('toMatrix') ], }, - // IntervalBounds + // IntervalEndpoints { input: [ [[1], [2]], @@ -1822,7 +1827,7 @@ g.test('toMatrix') interface AbsoluteErrorCase { value: number; error: number; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } // Special values used for testing absolute error interval @@ -1859,20 +1864,20 @@ g.test('absoluteErrorInterval') // 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]}, @@ -1942,7 +1947,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 +2039,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 +2079,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 @@ -2099,21 +2104,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 +2183,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 +2193,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 +2206,7 @@ const kConstantCorrectlyRoundedExpectation = { interface ScalarToIntervalCase { input: number; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('absInterval') @@ -2224,8 +2229,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 +2295,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 +2352,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 +2398,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 }, ]; }) ) @@ -2437,25 +2443,26 @@ g.test('asinInterval') const abs_error = p.trait === 'f32' ? 6.77e-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 +2507,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 +2576,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 +2626,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 }, ]; }) ) @@ -2674,8 +2681,8 @@ 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 }, @@ -2714,12 +2721,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 +2747,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 +2803,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 }, ]; }) ) @@ -2869,11 +2876,11 @@ g.test('degreesInterval') 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 +2902,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 +2923,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 +2961,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 +2981,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 +3058,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 }, @@ -3117,8 +3124,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 +3187,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 +3222,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 +3230,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 +3250,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 +3307,8 @@ g.test('logInterval') .expandWithParams(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 +3355,8 @@ g.test('log2Interval') .expandWithParams(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 +3394,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 +3432,8 @@ g.test('quantizeToF16Interval') .paramsSubcasesOnly( // 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 +3451,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 => { @@ -3514,9 +3521,9 @@ g.test('radiansInterval') 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] ]; }) @@ -3579,8 +3586,8 @@ 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 }, @@ -3635,8 +3642,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 }, ]; }) ) @@ -3659,7 +3666,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 }, @@ -3675,7 +3682,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 }, ]; }) ) @@ -3704,13 +3711,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 }, ]; }) ) @@ -3758,10 +3765,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 }, ]; }) ) @@ -3840,9 +3847,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 }, ]; }) ) @@ -3921,12 +3928,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 }, ]; }) ) @@ -3968,10 +3975,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 }, ]; }) ) @@ -4015,8 +4022,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 }, @@ -4038,7 +4045,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 @@ -4120,14 +4127,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 }, ]; }) ) @@ -4234,33 +4241,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 }, ]; }) ) @@ -4298,25 +4305,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 }, ]; }) ) @@ -4405,15 +4412,15 @@ g.test('divisionInterval') ...kDivisionInterval64BitsNormalCases[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 }, ]; }) ) @@ -4460,11 +4467,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 @@ -4486,11 +4493,11 @@ 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[], } as const; @@ -4520,8 +4527,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 }, ]; }) ) @@ -4580,14 +4587,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 }, ]; }) ) @@ -4646,14 +4653,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 }, ]; }) ) @@ -4748,22 +4755,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 }, ]; }) ) @@ -4816,11 +4823,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], ]; @@ -4886,15 +4893,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 }, ]; }) ) @@ -4970,14 +4977,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 }, ]; }) ) @@ -5068,14 +5075,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 }, ]; }) ) @@ -5092,7 +5099,7 @@ g.test('subtractionInterval') interface ScalarTripleToIntervalCase { input: [number, number, number]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('clampMedianInterval') @@ -5135,10 +5142,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 }, ]; }) ) @@ -5193,10 +5200,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 }, ]; }) ) @@ -5294,11 +5301,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], ]; }) @@ -5365,7 +5372,7 @@ const kMixImpreciseIntervalCases = { // 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 }, + { 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] }, @@ -5420,16 +5427,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 }, + { 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 }, // The [negative.min, +/-10.0, 1.0] cases has different result for different trait on // imprecise version. @@ -5535,16 +5542,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 }, + { 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 }, // Showing how precise and imprecise versions diff { input: [constants.negative.min, 10.0, 1.0], expected: 10.0 }, @@ -5630,18 +5637,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 }, ]; }) ) @@ -5658,7 +5665,7 @@ g.test('smoothStepInterval') interface ScalarToVectorCase { input: number; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } g.test('unpack2x16floatInterval') @@ -5682,8 +5689,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 => { @@ -5699,24 +5706,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 @@ -5725,13 +5732,13 @@ g.test('unpack2x16floatInterval') .paramsSubcasesOnly( // 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 => { @@ -5748,15 +5755,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 @@ -5765,11 +5772,11 @@ g.test('unpack2x16floatInterval') .paramsSubcasesOnly( // 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 => { @@ -5786,23 +5793,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 @@ -5811,27 +5818,27 @@ g.test('unpack2x16floatInterval') .paramsSubcasesOnly( // 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] }, ] ) @@ -5849,15 +5856,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 @@ -5866,19 +5873,19 @@ g.test('unpack2x16floatInterval') .paramsSubcasesOnly( // 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] }, ] ) @@ -5894,7 +5901,7 @@ g.test('unpack2x16floatInterval') interface VectorToIntervalCase { input: number[]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('lengthIntervalVector') @@ -5934,10 +5941,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 }, ]; }) ) @@ -5953,7 +5960,7 @@ g.test('lengthIntervalVector') interface VectorPairToIntervalCase { input: [number[], number[]]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('distanceIntervalVector') @@ -5964,11 +5971,11 @@ g.test('distanceIntervalVector') .expandWithParams(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 @@ -5977,7 +5984,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 @@ -5992,7 +5999,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 @@ -6027,7 +6034,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] }, @@ -6037,10 +6044,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; @@ -6084,12 +6091,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 }, ]; }) ) @@ -6106,7 +6113,7 @@ g.test('dotInterval') interface VectorToVectorCase { input: number[]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } // prettier-ignore @@ -6177,7 +6184,7 @@ g.test('normalizeInterval') interface VectorPairToVectorCase { input: [number[], number[]]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } // prettier-ignore @@ -6345,16 +6352,16 @@ 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] }, ]; }) ) @@ -6373,7 +6380,7 @@ g.test('reflectInterval') interface MatrixToScalarCase { input: number[][]; - expected: number | IntervalBounds; + expected: number | IntervalEndpoints; } g.test('determinantInterval') @@ -6488,7 +6495,7 @@ g.test('determinantInterval') interface MatrixToMatrixCase { input: number[][]; - expected: (number | IntervalBounds)[][]; + expected: (number | IntervalEndpoints)[][]; } g.test('transposeInterval') @@ -6642,7 +6649,7 @@ g.test('transposeInterval') interface MatrixPairToMatrixCase { input: [number[][], number[][]]; - expected: (number | IntervalBounds)[][]; + expected: (number | IntervalEndpoints)[][]; } g.test('additionMatrixMatrixInterval') @@ -7585,7 +7592,7 @@ g.test('multiplicationMatrixMatrixInterval') interface MatrixScalarToMatrixCase { matrix: number[][]; scalar: number; - expected: (number | IntervalBounds)[][]; + expected: (number | IntervalEndpoints)[][]; } const kMultiplicationMatrixScalarIntervalCases = { @@ -7774,7 +7781,7 @@ g.test('multiplicationMatrixScalarInterval') interface MatrixVectorToVectorCase { matrix: number[][]; vector: number[]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } g.test('multiplicationMatrixVectorInterval') @@ -7891,7 +7898,7 @@ g.test('multiplicationMatrixVectorInterval') interface VectorMatrixToVectorCase { vector: number[]; matrix: number[][]; - expected: (number | IntervalBounds)[]; + expected: (number | IntervalEndpoints)[]; } g.test('multiplicationVectorMatrixInterval') @@ -8010,7 +8017,7 @@ g.test('multiplicationVectorMatrixInterval') interface FaceForwardCase { input: [number[], number[], number[]]; - expected: ((number | IntervalBounds)[] | undefined)[]; + expected: ((number | IntervalEndpoints)[] | undefined)[]; } g.test('faceForwardIntervals') @@ -8089,8 +8096,8 @@ g.test('faceForwardIntervals') interface ModfCase { input: number; - fract: number | IntervalBounds; - whole: number | IntervalBounds; + fract: number | IntervalEndpoints; + whole: number | IntervalEndpoints; } g.test('modfInterval') @@ -8143,18 +8150,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 @@ -8186,7 +8193,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] @@ -8213,23 +8220,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/src/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index ed70413f28ab..5880a7593775 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -99,12 +99,12 @@ export function deserializeFPKind(s: BinaryStream): FPKind { // Containers /** - * Representation of bounds for an interval as an array with either one or two - * elements. Single element indicates that the interval is a single point. For - * two elements, the first is the lower bound of the interval and the second is - * the upper bound. + * Representation of endpoints for an interval as an array with either one or + * two elements. Single element indicates that the interval is a single point. + * For two elements, the first is the lower edges of the interval and the + * second is the upper edge, i.e. e[0] <= e[1], where e is an IntervalEndpoints */ -export type IntervalBounds = readonly [number] | readonly [number, number]; +export type IntervalEndpoints = readonly [number] | readonly [number, number]; /** Represents a closed interval of floating point numbers */ export class FPInterval { @@ -118,15 +118,18 @@ export class FPInterval { * `FPTraits.toInterval` is the preferred way to create FPIntervals * * @param kind the floating point number type this is an interval for - * @param bounds beginning and end of the interval + * @param endpoints beginning and end of the interval */ - public constructor(kind: FPKind, ...bounds: IntervalBounds) { + public constructor(kind: FPKind, ...endpoints: IntervalEndpoints) { this.kind = kind; - const begin = bounds[0]; - const end = bounds.length === 2 ? bounds[1] : bounds[0]; - assert(!Number.isNaN(begin) && !Number.isNaN(end), `bounds need to be non-NaN`); - assert(begin <= end, `bounds[0] (${begin}) must be less than or equal to bounds[1] (${end})`); + const begin = endpoints[0]; + const end = endpoints.length === 2 ? endpoints[1] : endpoints[0]; + assert(!Number.isNaN(begin) && !Number.isNaN(end), `endpoints need to be non-NaN`); + assert( + begin <= end, + `endpoints[0] (${begin}) must be less than or equal to endpoints[1] (${end})` + ); this.begin = begin; this.end = end; @@ -138,7 +141,7 @@ export class FPInterval { } /** @returns begin and end if non-point interval, otherwise just begin */ - public bounds(): IntervalBounds { + public endpoints(): IntervalEndpoints { return this.isPoint() ? [this.begin] : [this.begin, this.end]; } @@ -179,7 +182,7 @@ export class FPInterval { /** @returns a string representation for logging purposes */ public toString(): string { - return `{ '${this.kind}', [${this.bounds().map(this.traits().scalarBuilder)}] }`; + return `{ '${this.kind}', [${this.endpoints().map(this.traits().scalarBuilder)}] }`; } } @@ -328,7 +331,7 @@ interface ScalarToIntervalOp { * occur and returns a span of those points to be used as the domain instead. * * Used by this.runScalarToIntervalOp before invoking impl. - * If not defined, the bounds of the existing domain are assumed to be the + * If not defined, the endpoints of the existing domain are assumed to be the * extrema. * * This is only implemented for operations that meet all the following @@ -359,10 +362,10 @@ interface ScalarPairToIntervalOp { * occur and returns spans of those points to be used as the domain instead. * * Used by runScalarPairToIntervalOp before invoking impl. - * If not defined, the bounds of the existing domain are assumed to be the + * If not defined, the endpoints of the existing domain are assumed to be the * extrema. * - * This is only implemented for functions that meet all of the following + * This is only implemented for functions that meet all the following * criteria: * a) non-monotonic * b) used in inherited accuracy calculations @@ -572,7 +575,7 @@ export interface VectorMatrixToVector { // Traits /** - * Typed structure containing all the limits/constants defined for each + * Typed structure containing all the constants defined for each * WGSL floating point kind */ interface FPConstants { @@ -651,7 +654,7 @@ interface FPConstants { /** A representation of an FPInterval for a case param */ export type FPIntervalParam = { kind: FPKind; - interval: number | IntervalBounds; + interval: number | IntervalEndpoints; }; /** Abstract base class for all floating-point traits */ @@ -666,7 +669,7 @@ export abstract class FPTraits { // Utilities - Implemented /** @returns an interval containing the point or the original interval */ - public toInterval(n: number | IntervalBounds | FPInterval): FPInterval { + public toInterval(n: number | IntervalEndpoints | FPInterval): FPInterval { if (n instanceof FPInterval) { if (n.kind === this.kind) { return n; @@ -677,7 +680,7 @@ export abstract class FPTraits { return this.constants().unboundedInterval; } - return new FPInterval(this.kind, ...n.bounds()); + return new FPInterval(this.kind, ...n.endpoints()); } if (n instanceof Array) { @@ -690,7 +693,7 @@ export abstract class FPTraits { /** * Makes a param that can be turned into an interval */ - public toParam(n: number | IntervalBounds): FPIntervalParam { + public toParam(n: number | IntervalEndpoints): FPIntervalParam { return { kind: this.kind, interval: n, @@ -701,18 +704,18 @@ export abstract class FPTraits { * Converts p into an FPInterval if it is an FPIntervalPAram */ public fromParam( - p: number | IntervalBounds | FPIntervalParam - ): number | IntervalBounds | FPInterval { + p: number | IntervalEndpoints | FPIntervalParam + ): number | IntervalEndpoints | FPInterval { const param = p as FPIntervalParam; if (param.interval && param.kind) { assert(param.kind === this.kind); return this.toInterval(param.interval); } - return p as number | IntervalBounds; + return p as number | IntervalEndpoints; } /** - * @returns an interval with the tightest bounds that includes all provided + * @returns an interval with the tightest endpoints that includes all provided * intervals */ public spanIntervals(...intervals: readonly FPInterval[]): FPInterval { @@ -731,7 +734,7 @@ export abstract class FPTraits { } /** Narrow an array of values to FPVector if possible */ - public isVector(v: ReadonlyArray): v is FPVector { + public isVector(v: ReadonlyArray): v is FPVector { if (v.every(e => e instanceof FPInterval && e.kind === this.kind)) { return v.length === 2 || v.length === 3 || v.length === 4; } @@ -739,7 +742,7 @@ export abstract class FPTraits { } /** @returns an FPVector representation of an array of values if possible */ - public toVector(v: ReadonlyArray): FPVector { + public toVector(v: ReadonlyArray): FPVector { if (this.isVector(v) && v.every(e => e.kind === this.kind)) { return v; } @@ -778,7 +781,7 @@ export abstract class FPTraits { } /** Narrow an array of an array of values to FPMatrix if possible */ - public isMatrix(m: Array2D | FPVector[]): m is FPMatrix { + public isMatrix(m: Array2D | FPVector[]): m is FPMatrix { if (!m.every(c => c.every(e => e instanceof FPInterval && e.kind === this.kind))) { return false; } @@ -803,7 +806,7 @@ export abstract class FPTraits { } /** @returns an FPMatrix representation of an array of an array of values if possible */ - public toMatrix(m: Array2D | FPVector[]): FPMatrix { + public toMatrix(m: Array2D | FPVector[]): FPMatrix { if ( this.isMatrix(m) && every2DArray(m, (e: FPInterval) => { @@ -2282,7 +2285,7 @@ export abstract class FPTraits { } const result = this.spanIntervals( - ...x.bounds().map(b => this.roundAndFlushScalarToInterval(b, op)) + ...x.endpoints().map(b => this.roundAndFlushScalarToInterval(b, op)) ); return result.isFinite() ? result : this.constants().unboundedInterval; } @@ -2312,8 +2315,8 @@ export abstract class FPTraits { } const outputs = new Set(); - x.bounds().forEach(inner_x => { - y.bounds().forEach(inner_y => { + x.endpoints().forEach(inner_x => { + y.endpoints().forEach(inner_y => { outputs.add(this.roundAndFlushScalarPairToInterval(inner_x, inner_y, op)); }); }); @@ -2342,9 +2345,9 @@ export abstract class FPTraits { } const outputs = new Set(); - x.bounds().forEach(inner_x => { - y.bounds().forEach(inner_y => { - z.bounds().forEach(inner_z => { + x.endpoints().forEach(inner_x => { + y.endpoints().forEach(inner_y => { + z.endpoints().forEach(inner_z => { outputs.add(this.roundAndFlushScalarTripleToInterval(inner_x, inner_y, inner_z, op)); }); }); @@ -2367,7 +2370,7 @@ export abstract class FPTraits { return this.constants().unboundedInterval; } - const x_values = cartesianProduct(...x.map(e => e.bounds())); + const x_values = cartesianProduct(...x.map(e => e.endpoints())); const outputs = new Set(); x_values.forEach(inner_x => { @@ -2396,8 +2399,8 @@ export abstract class FPTraits { return this.constants().unboundedInterval; } - const x_values = cartesianProduct(...x.map(e => e.bounds())); - const y_values = cartesianProduct(...y.map(e => e.bounds())); + const x_values = cartesianProduct(...x.map(e => e.endpoints())); + const y_values = cartesianProduct(...y.map(e => e.endpoints())); const outputs = new Set(); x_values.forEach(inner_x => { @@ -2423,7 +2426,7 @@ export abstract class FPTraits { return this.constants().unboundedVector[x.length]; } - const x_values = cartesianProduct(...x.map(e => e.bounds())); + const x_values = cartesianProduct(...x.map(e => e.endpoints())); const outputs = new Set(); x_values.forEach(inner_x => { @@ -2467,8 +2470,8 @@ export abstract class FPTraits { return this.constants().unboundedVector[x.length]; } - const x_values = cartesianProduct(...x.map(e => e.bounds())); - const y_values = cartesianProduct(...y.map(e => e.bounds())); + const x_values = cartesianProduct(...x.map(e => e.endpoints())); + const y_values = cartesianProduct(...y.map(e => e.endpoints())); const outputs = new Set(); x_values.forEach(inner_x => { @@ -2530,7 +2533,9 @@ export abstract class FPTraits { } const m_flat: readonly FPInterval[] = flatten2DArray(m); - const m_values: ROArrayArray = cartesianProduct(...m_flat.map(e => e.bounds())); + const m_values: ROArrayArray = cartesianProduct( + ...m_flat.map(e => e.endpoints()) + ); const outputs = new Set(); m_values.forEach(inner_m => { @@ -3571,9 +3576,9 @@ export abstract class FPTraits { if (e2 > bias + 1) { return this.constants().unboundedInterval; } - // The spec says the result of ldexp(e1, e2) = e1 * 2 ^ e2, and the accuracy is correctly - // rounded to the true value, so the inheritance framework does not need to be invoked to - // determine bounds. + // The spec says the result of ldexp(e1, e2) = e1 * 2 ^ e2, and the + // accuracy is correctly rounded to the true value, so the inheritance + // framework does not need to be invoked to determine endpoints. // Instead, the value at a higher precision is calculated and passed to // correctlyRoundedInterval. const result = e1 * 2 ** e2; @@ -4721,7 +4726,7 @@ class F32Traits extends FPTraits { private unpack2x16floatIntervalImpl(n: number): FPVector { assert( n >= kValue.u32.min && n <= kValue.u32.max, - 'unpack2x16floatInterval only accepts values on the bounds of u32' + 'unpack2x16floatInterval only accepts valid u32 values' ); this.unpackDataU32[0] = n; if (this.unpackDataF16.some(f => !isFiniteF16(f))) { @@ -4745,7 +4750,7 @@ class F32Traits extends FPTraits { private unpack2x16snormIntervalImpl(n: number): FPVector { assert( n >= kValue.u32.min && n <= kValue.u32.max, - 'unpack2x16snormInterval only accepts values on the bounds of u32' + 'unpack2x16snormInterval only accepts valid u32 values' ); const op = (n: number): FPInterval => { return this.ulpInterval(Math.max(n / 32767, -1), 3); @@ -4761,7 +4766,7 @@ class F32Traits extends FPTraits { private unpack2x16unormIntervalImpl(n: number): FPVector { assert( n >= kValue.u32.min && n <= kValue.u32.max, - 'unpack2x16unormInterval only accepts values on the bounds of u32' + 'unpack2x16unormInterval only accepts valid u32 values' ); const op = (n: number): FPInterval => { return this.ulpInterval(n / 65535, 3); @@ -4777,7 +4782,7 @@ class F32Traits extends FPTraits { private unpack4x8snormIntervalImpl(n: number): FPVector { assert( n >= kValue.u32.min && n <= kValue.u32.max, - 'unpack4x8snormInterval only accepts values on the bounds of u32' + 'unpack4x8snormInterval only accepts valid u32 values' ); const op = (n: number): FPInterval => { return this.ulpInterval(Math.max(n / 127, -1), 3); @@ -4797,7 +4802,7 @@ class F32Traits extends FPTraits { private unpack4x8unormIntervalImpl(n: number): FPVector { assert( n >= kValue.u32.min && n <= kValue.u32.max, - 'unpack4x8unormInterval only accepts values on the bounds of u32' + 'unpack4x8unormInterval only accepts valid u32 values' ); const op = (n: number): FPInterval => { return this.ulpInterval(n / 255, 3);