From 9bdd0639033cff13b29dab062c523ad586b22170 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Tue, 5 Dec 2023 10:28:20 -0500 Subject: [PATCH] wgsl: Rework how input domain domains are limited Changes from wrap and forward pattern to have an optional utility function that returns the acceptable domains. This helps simplify reasoning about object lifetimes. Fixes #3201 --- src/resources/cache/hashes.json | 180 ++++++++++----------- src/webgpu/util/floating_point.ts | 256 +++++++++++++++--------------- 2 files changed, 220 insertions(+), 216 deletions(-) diff --git a/src/resources/cache/hashes.json b/src/resources/cache/hashes.json index 88a4ab4478b9..43aa1859e581 100644 --- a/src/resources/cache/hashes.json +++ b/src/resources/cache/hashes.json @@ -1,103 +1,103 @@ { - "webgpu/shader/execution/binary/af_addition.bin": "9ff4c694", + "webgpu/shader/execution/binary/af_addition.bin": "cbafc82c", "webgpu/shader/execution/binary/af_logical.bin": "9ec85311", - "webgpu/shader/execution/binary/af_division.bin": "573bcb2", - "webgpu/shader/execution/binary/af_matrix_addition.bin": "b82c13e3", - "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "54653771", - "webgpu/shader/execution/binary/af_multiplication.bin": "6c680224", - "webgpu/shader/execution/binary/af_remainder.bin": "e780a0c8", - "webgpu/shader/execution/binary/af_subtraction.bin": "315b6080", - "webgpu/shader/execution/binary/f16_addition.bin": "9e6f8c87", + "webgpu/shader/execution/binary/af_division.bin": "f89905b3", + "webgpu/shader/execution/binary/af_matrix_addition.bin": "3e99f56e", + "webgpu/shader/execution/binary/af_matrix_subtraction.bin": "ee161b6", + "webgpu/shader/execution/binary/af_multiplication.bin": "a22cdaa9", + "webgpu/shader/execution/binary/af_remainder.bin": "b874f4ec", + "webgpu/shader/execution/binary/af_subtraction.bin": "778e0c61", + "webgpu/shader/execution/binary/f16_addition.bin": "2d0950e3", "webgpu/shader/execution/binary/f16_logical.bin": "1edd08ec", - "webgpu/shader/execution/binary/f16_division.bin": "99dcc71b", - "webgpu/shader/execution/binary/f16_matrix_addition.bin": "3aa70dc8", - "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "2301ffc4", - "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "e97285fb", - "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "8e17ece5", - "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "abc984c1", - "webgpu/shader/execution/binary/f16_multiplication.bin": "f8c1e34c", - "webgpu/shader/execution/binary/f16_remainder.bin": "6bbb566f", - "webgpu/shader/execution/binary/f16_subtraction.bin": "f8acc55b", - "webgpu/shader/execution/binary/f32_addition.bin": "b9dfd280", + "webgpu/shader/execution/binary/f16_division.bin": "7aefab2", + "webgpu/shader/execution/binary/f16_matrix_addition.bin": "29ac151b", + "webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "3d1260a7", + "webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "4bbb407c", + "webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "1300adb5", + "webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "ca0c57ad", + "webgpu/shader/execution/binary/f16_multiplication.bin": "32d97a5f", + "webgpu/shader/execution/binary/f16_remainder.bin": "d5452448", + "webgpu/shader/execution/binary/f16_subtraction.bin": "7fed0126", + "webgpu/shader/execution/binary/f32_addition.bin": "6ba12f18", "webgpu/shader/execution/binary/f32_logical.bin": "fab7cfc5", - "webgpu/shader/execution/binary/f32_division.bin": "5bb9f335", - "webgpu/shader/execution/binary/f32_matrix_addition.bin": "e35631d2", - "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "b306d17", - "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "a62498ea", - "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "ae6edc24", - "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "3c64a590", - "webgpu/shader/execution/binary/f32_multiplication.bin": "2ede2dcd", - "webgpu/shader/execution/binary/f32_remainder.bin": "99df37d5", - "webgpu/shader/execution/binary/f32_subtraction.bin": "dce728b3", + "webgpu/shader/execution/binary/f32_division.bin": "8e4a63bb", + "webgpu/shader/execution/binary/f32_matrix_addition.bin": "454ae6ca", + "webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "8edc9ed2", + "webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "eb8bab4d", + "webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "438f6f2c", + "webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "7c60fba5", + "webgpu/shader/execution/binary/f32_multiplication.bin": "5985aa4d", + "webgpu/shader/execution/binary/f32_remainder.bin": "d03a6f99", + "webgpu/shader/execution/binary/f32_subtraction.bin": "73b6dc49", "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": "b4140bc8", - "webgpu/shader/execution/acos.bin": "82621bf", - "webgpu/shader/execution/acosh.bin": "4166d51", - "webgpu/shader/execution/asin.bin": "3db84c59", - "webgpu/shader/execution/asinh.bin": "5aa1e7a2", - "webgpu/shader/execution/atan.bin": "de8094d", - "webgpu/shader/execution/atan2.bin": "9b5938b4", - "webgpu/shader/execution/atanh.bin": "3fa65838", - "webgpu/shader/execution/bitcast.bin": "e4e06164", - "webgpu/shader/execution/ceil.bin": "4746da0e", - "webgpu/shader/execution/clamp.bin": "56745472", - "webgpu/shader/execution/cos.bin": "e1ed11", - "webgpu/shader/execution/cosh.bin": "fbd92f9", - "webgpu/shader/execution/cross.bin": "3ade16cf", - "webgpu/shader/execution/degrees.bin": "1392cbd6", - "webgpu/shader/execution/determinant.bin": "5b562124", - "webgpu/shader/execution/distance.bin": "48c26875", - "webgpu/shader/execution/dot.bin": "784b83f1", - "webgpu/shader/execution/exp.bin": "bf0dcc36", - "webgpu/shader/execution/exp2.bin": "2a7bbe9e", - "webgpu/shader/execution/faceForward.bin": "e14093b7", - "webgpu/shader/execution/floor.bin": "7ac9dae9", - "webgpu/shader/execution/fma.bin": "172e6a74", - "webgpu/shader/execution/fract.bin": "cbe80708", - "webgpu/shader/execution/frexp.bin": "d3ce34ce", - "webgpu/shader/execution/inverseSqrt.bin": "a65b3762", - "webgpu/shader/execution/ldexp.bin": "aa39225f", - "webgpu/shader/execution/length.bin": "67457bbf", - "webgpu/shader/execution/log.bin": "1ffe589b", - "webgpu/shader/execution/log2.bin": "451a8eb8", - "webgpu/shader/execution/max.bin": "e96b1472", - "webgpu/shader/execution/min.bin": "2f429b46", - "webgpu/shader/execution/mix.bin": "2a438cc0", - "webgpu/shader/execution/modf.bin": "94a8dad4", - "webgpu/shader/execution/normalize.bin": "9deeb24e", + "webgpu/shader/execution/abs.bin": "7592c2e9", + "webgpu/shader/execution/acos.bin": "ffa331d7", + "webgpu/shader/execution/acosh.bin": "8bc525c1", + "webgpu/shader/execution/asin.bin": "c40b93d8", + "webgpu/shader/execution/asinh.bin": "83a941a2", + "webgpu/shader/execution/atan.bin": "1a597eec", + "webgpu/shader/execution/atan2.bin": "225c7bf5", + "webgpu/shader/execution/atanh.bin": "a0a50a1", + "webgpu/shader/execution/bitcast.bin": "fb5cbfe6", + "webgpu/shader/execution/ceil.bin": "9b74d951", + "webgpu/shader/execution/clamp.bin": "e21840ce", + "webgpu/shader/execution/cos.bin": "716ae7de", + "webgpu/shader/execution/cosh.bin": "2caa57a3", + "webgpu/shader/execution/cross.bin": "3cd2bb7d", + "webgpu/shader/execution/degrees.bin": "854e3b43", + "webgpu/shader/execution/determinant.bin": "e393a3d6", + "webgpu/shader/execution/distance.bin": "ab1efb22", + "webgpu/shader/execution/dot.bin": "dca4281b", + "webgpu/shader/execution/exp.bin": "229d2470", + "webgpu/shader/execution/exp2.bin": "29bcb796", + "webgpu/shader/execution/faceForward.bin": "5b501473", + "webgpu/shader/execution/floor.bin": "4c2cbe16", + "webgpu/shader/execution/fma.bin": "eb0531af", + "webgpu/shader/execution/fract.bin": "b94ef628", + "webgpu/shader/execution/frexp.bin": "5e1f6865", + "webgpu/shader/execution/inverseSqrt.bin": "d793704a", + "webgpu/shader/execution/ldexp.bin": "38e2de2e", + "webgpu/shader/execution/length.bin": "1e248428", + "webgpu/shader/execution/log.bin": "77a9e6ce", + "webgpu/shader/execution/log2.bin": "62a8591f", + "webgpu/shader/execution/max.bin": "a6e79c98", + "webgpu/shader/execution/min.bin": "8d202927", + "webgpu/shader/execution/mix.bin": "92b9d5a6", + "webgpu/shader/execution/modf.bin": "a7865848", + "webgpu/shader/execution/normalize.bin": "6060c1b7", "webgpu/shader/execution/pack2x16float.bin": "b2cb12ea", - "webgpu/shader/execution/pow.bin": "861845e1", - "webgpu/shader/execution/quantizeToF16.bin": "cdec1b52", - "webgpu/shader/execution/radians.bin": "ff93c962", - "webgpu/shader/execution/reflect.bin": "da8f8021", - "webgpu/shader/execution/refract.bin": "918d854e", - "webgpu/shader/execution/round.bin": "314d28fe", - "webgpu/shader/execution/saturate.bin": "3eff93e8", - "webgpu/shader/execution/sign.bin": "fcc68d98", - "webgpu/shader/execution/sin.bin": "cc92b580", - "webgpu/shader/execution/sinh.bin": "751d3edb", - "webgpu/shader/execution/smoothstep.bin": "2604a849", - "webgpu/shader/execution/sqrt.bin": "100a05ab", - "webgpu/shader/execution/step.bin": "9463ab1b", - "webgpu/shader/execution/tan.bin": "8c924dc6", - "webgpu/shader/execution/tanh.bin": "c73a178", - "webgpu/shader/execution/transpose.bin": "21358779", - "webgpu/shader/execution/trunc.bin": "38d0c22e", - "webgpu/shader/execution/unpack2x16float.bin": "8138f397", - "webgpu/shader/execution/unpack2x16snorm.bin": "23d2c366", - "webgpu/shader/execution/unpack2x16unorm.bin": "ffed85bc", - "webgpu/shader/execution/unpack4x8snorm.bin": "6ac8cab7", - "webgpu/shader/execution/unpack4x8unorm.bin": "3cebb66b", - "webgpu/shader/execution/unary/af_arithmetic.bin": "e4193a8f", - "webgpu/shader/execution/unary/af_assignment.bin": "c841e6c6", + "webgpu/shader/execution/pow.bin": "9cafb208", + "webgpu/shader/execution/quantizeToF16.bin": "e9fe32d3", + "webgpu/shader/execution/radians.bin": "3de6e3e2", + "webgpu/shader/execution/reflect.bin": "ada28c0e", + "webgpu/shader/execution/refract.bin": "1bfc8954", + "webgpu/shader/execution/round.bin": "d6980b30", + "webgpu/shader/execution/saturate.bin": "ac1c87b7", + "webgpu/shader/execution/sign.bin": "8159fff8", + "webgpu/shader/execution/sin.bin": "57eef8dd", + "webgpu/shader/execution/sinh.bin": "fe8c4a10", + "webgpu/shader/execution/smoothstep.bin": "5edbb429", + "webgpu/shader/execution/sqrt.bin": "c56cd7f8", + "webgpu/shader/execution/step.bin": "5dba0cdb", + "webgpu/shader/execution/tan.bin": "a130ad0d", + "webgpu/shader/execution/tanh.bin": "3129d901", + "webgpu/shader/execution/transpose.bin": "a9009349", + "webgpu/shader/execution/trunc.bin": "7e345f71", + "webgpu/shader/execution/unpack2x16float.bin": "84d23fa6", + "webgpu/shader/execution/unpack2x16snorm.bin": "df585faa", + "webgpu/shader/execution/unpack2x16unorm.bin": "7e5b4b66", + "webgpu/shader/execution/unpack4x8snorm.bin": "fdd14e11", + "webgpu/shader/execution/unpack4x8unorm.bin": "a095fd3d", + "webgpu/shader/execution/unary/af_arithmetic.bin": "92cf0ae3", + "webgpu/shader/execution/unary/af_assignment.bin": "6345d35", "webgpu/shader/execution/unary/bool_conversion.bin": "cb53bf65", - "webgpu/shader/execution/unary/f16_arithmetic.bin": "59fe7c00", - "webgpu/shader/execution/unary/f16_conversion.bin": "428ba8c1", - "webgpu/shader/execution/unary/f32_arithmetic.bin": "ba5faac0", - "webgpu/shader/execution/unary/f32_conversion.bin": "dd493eea", + "webgpu/shader/execution/unary/f16_arithmetic.bin": "c913cc95", + "webgpu/shader/execution/unary/f16_conversion.bin": "fd520112", + "webgpu/shader/execution/unary/f32_arithmetic.bin": "e113d162", + "webgpu/shader/execution/unary/f32_conversion.bin": "26505ced", "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/webgpu/util/floating_point.ts b/src/webgpu/util/floating_point.ts index d8e5f3b43413..726afc588350 100644 --- a/src/webgpu/util/floating_point.ts +++ b/src/webgpu/util/floating_point.ts @@ -342,6 +342,14 @@ interface ScalarToIntervalOp { * i.e. fooInterval takes in x: number | FPInterval, not x: number */ extrema?: (x: FPInterval) => FPInterval; + + /** + * Restricts the inputs to operation to the given domain. + * + * Only defined for operations that have tighter domain requirements than 'must + * be finite'. + */ + domain?: () => FPInterval; } /** @@ -353,6 +361,13 @@ export interface ScalarPairToInterval { (x: number, y: number): FPInterval; } +/** Domain for a ScalarPairToInterval implementation */ +interface ScalarPairToIntervalDomain { + // Arrays to support discrete valid domain intervals + x: readonly FPInterval[]; + y: readonly FPInterval[]; +} + /** Operation used to implement a ScalarPairToInterval */ interface ScalarPairToIntervalOp { /** @returns acceptance interval for a function at point (x, y) */ @@ -372,13 +387,14 @@ interface ScalarPairToIntervalOp { * c) need to take in an interval for b) */ extrema?: (x: FPInterval, y: FPInterval) => [FPInterval, FPInterval]; -} -/** Domain for a ScalarPairToInterval implementation */ -interface ScalarPairToIntervalDomain { - // Arrays to support discrete valid domain intervals - x: readonly FPInterval[]; - y: readonly FPInterval[]; + /** + * Restricts the inputs to operation to the given domain. + * + * Only defined for operations that have tighter domain requirements than 'must + * be finite'. + */ + domain?: () => ScalarPairToIntervalDomain; } /** @@ -622,6 +638,7 @@ interface FPConstants { zeroInterval: FPInterval; negPiToPiInterval: FPInterval; greaterThanZeroInterval: FPInterval; + negOneToOneInterval: FPInterval; zeroVector: { 2: FPVector; 3: FPVector; @@ -859,48 +876,6 @@ export abstract class FPTraits { return needs_zero ? values.concat(0) : values; } - /** - * Restrict the inputs to an ScalarToInterval operation - * - * Only used for operations that have tighter domain requirements than 'must - * be finite'. - * - * @param domain interval to restrict inputs to - * @param impl operation implementation to run if input is within the required domain - * @returns a ScalarToInterval that calls impl if domain contains the input, - * otherwise it returns an unbounded interval */ - protected limitScalarToIntervalDomain( - domain: FPInterval, - impl: ScalarToInterval - ): ScalarToInterval { - return (n: number): FPInterval => { - return domain.contains(n) ? impl(n) : this.constants().unboundedInterval; - }; - } - - /** - * Restrict the inputs to a ScalarPairToInterval - * - * Only used for operations that have tighter domain requirements than 'must be - * finite'. - * - * @param domain set of intervals to restrict inputs to - * @param impl operation implementation to run if input is within the required domain - * @returns a ScalarPairToInterval that calls impl if domain contains the input, - * otherwise it returns an unbounded interval */ - protected limitScalarPairToIntervalDomain( - domain: ScalarPairToIntervalDomain, - impl: ScalarPairToInterval - ): ScalarPairToInterval { - return (x: number, y: number): FPInterval => { - if (!domain.x.some(d => d.contains(x)) || !domain.y.some(d => d.contains(y))) { - return this.constants().unboundedInterval; - } - - return impl(x, y); - }; - } - /** Stub for scalar to interval generator */ protected unimplementedScalarToInterval(name: string, _x: number | FPInterval): FPInterval { unreachable(`'${name}' is not yet implemented for '${this.kind}'`); @@ -2006,6 +1981,15 @@ export abstract class FPTraits { assert(!Number.isNaN(n), `flush not defined for NaN`); const values = this.correctlyRounded(n); const inputs = this.addFlushedIfNeeded(values); + + if (op.domain !== undefined) { + // Cannot invoke op.domain() directly in the .some, because the narrowing doesn't propegate. + const domain = op.domain(); + if (inputs.some(i => !domain.contains(i))) { + return this.constants().unboundedInterval; + } + } + const results = new Set(inputs.map(op.impl)); return this.spanIntervals(...results); } @@ -2031,10 +2015,25 @@ export abstract class FPTraits { ): FPInterval { assert(!Number.isNaN(x), `flush not defined for NaN`); assert(!Number.isNaN(y), `flush not defined for NaN`); + const x_values = this.correctlyRounded(x); const y_values = this.correctlyRounded(y); const x_inputs = this.addFlushedIfNeeded(x_values); const y_inputs = this.addFlushedIfNeeded(y_values); + + if (op.domain !== undefined) { + // Cannot invoke op.domain() directly in the .some, because the narrowing doesn't propegate. + const domain = op.domain(); + + if (x_inputs.some(i => !domain.x.some(e => e.contains(i)))) { + return this.constants().unboundedInterval; + } + + if (y_inputs.some(j => !domain.y.some(e => e.contains(j)))) { + return this.constants().unboundedInterval; + } + } + const intervals = new Set(); x_inputs.forEach(inner_x => { y_inputs.forEach(inner_y => { @@ -2708,7 +2707,7 @@ export abstract class FPTraits { // This op is implemented differently for f32 and f16. private readonly AcosIntervalOp: ScalarToIntervalOp = { - impl: this.limitScalarToIntervalDomain(this.toInterval([-1.0, 1.0]), (n: number) => { + impl: (n: number) => { assert(this.kind === 'f32' || this.kind === 'f16'); // acos(n) = atan2(sqrt(1.0 - n * n), n) or a polynomial approximation with absolute error const y = this.sqrtInterval(this.subtractionInterval(1, this.multiplicationInterval(n, n))); @@ -2717,7 +2716,10 @@ export abstract class FPTraits { this.atan2Interval(y, n), this.absoluteErrorInterval(Math.acos(n), approx_abs_error) ); - }), + }, + domain: () => { + return this.constants().negOneToOneInterval; + }, }; protected acosIntervalImpl(n: number): FPInterval { @@ -2801,7 +2803,7 @@ export abstract class FPTraits { // This op is implemented differently for f32 and f16. private readonly AsinIntervalOp: ScalarToIntervalOp = { - impl: this.limitScalarToIntervalDomain(this.toInterval([-1.0, 1.0]), (n: number) => { + impl: (n: number) => { assert(this.kind === 'f32' || this.kind === 'f16'); // asin(n) = atan2(n, sqrt(1.0 - n * n)) or a polynomial approximation with absolute error const x = this.sqrtInterval(this.subtractionInterval(1, this.multiplicationInterval(n, n))); @@ -2810,7 +2812,10 @@ export abstract class FPTraits { this.atan2Interval(n, x), this.absoluteErrorInterval(Math.asin(n), approx_abs_error) ); - }), + }, + domain: () => { + return this.constants().negOneToOneInterval; + }, }; /** Calculate an acceptance interval for asin(n) */ @@ -2871,29 +2876,23 @@ export abstract class FPTraits { : [this.toInterval([-(2 ** 14), -(2 ** -14)]), this.toInterval([2 ** -14, 2 ** 14])]; const ulp_error = this.kind === 'f32' ? 4096 : 5; return { - impl: this.limitScalarPairToIntervalDomain( - { - x: domain_x, - y: domain_y, - }, - (y: number, x: number): FPInterval => { - // Accurate result in f64 - let atan_yx = Math.atan(y / x); - // Offset by +/-pi according to the definition. Use pi value in f64 because we are - // handling accurate result. - if (x < 0) { - // x < 0, y > 0, result is atan(y/x) + π - if (y > 0) { - atan_yx = atan_yx + kValue.f64.positive.pi.whole; - } else { - // x < 0, y < 0, result is atan(y/x) - π - atan_yx = atan_yx - kValue.f64.positive.pi.whole; - } + impl: (y: number, x: number): FPInterval => { + // Accurate result in f64 + let atan_yx = Math.atan(y / x); + // Offset by +/-pi according to the definition. Use pi value in f64 because we are + // handling accurate result. + if (x < 0) { + // x < 0, y > 0, result is atan(y/x) + π + if (y > 0) { + atan_yx = atan_yx + kValue.f64.positive.pi.whole; + } else { + // x < 0, y < 0, result is atan(y/x) - π + atan_yx = atan_yx - kValue.f64.positive.pi.whole; } - - return this.ulpInterval(atan_yx, ulp_error); } - ), + + return this.ulpInterval(atan_yx, ulp_error); + }, extrema: (y: FPInterval, x: FPInterval): [FPInterval, FPInterval] => { // There is discontinuity, which generates an unbounded result, at y/x = 0 that will dominate the accuracy if (y.contains(0)) { @@ -2904,6 +2903,9 @@ export abstract class FPTraits { } return [y, x]; }, + domain: () => { + return { x: domain_x, y: domain_y }; + }, }; } @@ -3019,14 +3021,14 @@ export abstract class FPTraits { public abstract readonly clampIntervals: ScalarTripleToInterval[]; private readonly CosIntervalOp: ScalarToIntervalOp = { - impl: this.limitScalarToIntervalDomain( - this.constants().negPiToPiInterval, - (n: number): FPInterval => { - assert(this.kind === 'f32' || this.kind === 'f16'); - const abs_error = this.kind === 'f32' ? 2 ** -11 : 2 ** -7; - return this.absoluteErrorInterval(Math.cos(n), abs_error); - } - ), + impl: (n: number): FPInterval => { + assert(this.kind === 'f32' || this.kind === 'f16'); + const abs_error = this.kind === 'f32' ? 2 ** -11 : 2 ** -7; + return this.absoluteErrorInterval(Math.cos(n), abs_error); + }, + domain: () => { + return this.constants().negPiToPiInterval; + }, }; protected cosIntervalImpl(n: number): FPInterval { @@ -3316,18 +3318,12 @@ export abstract class FPTraits { ? [this.toInterval([-(2 ** 126), -(2 ** -126)]), this.toInterval([2 ** -126, 2 ** 126])] : [this.toInterval([-(2 ** 14), -(2 ** -14)]), this.toInterval([2 ** -14, 2 ** 14])]; return { - impl: this.limitScalarPairToIntervalDomain( - { - x: domain_x, - y: domain_y, - }, - (x: number, y: number): FPInterval => { - if (y === 0) { - return constants.unboundedInterval; - } - return this.ulpInterval(x / y, 2.5); + impl: (x: number, y: number): FPInterval => { + if (y === 0) { + return constants.unboundedInterval; } - ), + return this.ulpInterval(x / y, 2.5); + }, extrema: (x: FPInterval, y: FPInterval): [FPInterval, FPInterval] => { // division has a discontinuity at y = 0. if (y.contains(0)) { @@ -3335,6 +3331,9 @@ export abstract class FPTraits { } return [x, y]; }, + domain: () => { + return { x: domain_x, y: domain_y }; + }, }; } @@ -3552,12 +3551,12 @@ export abstract class FPTraits { public abstract readonly fractInterval: (n: number) => FPInterval; private readonly InverseSqrtIntervalOp: ScalarToIntervalOp = { - impl: this.limitScalarToIntervalDomain( - this.constants().greaterThanZeroInterval, - (n: number): FPInterval => { - return this.ulpInterval(1 / Math.sqrt(n), 2); - } - ), + impl: (n: number): FPInterval => { + return this.ulpInterval(1 / Math.sqrt(n), 2); + }, + domain: () => { + return this.constants().greaterThanZeroInterval; + }, }; protected inverseSqrtIntervalImpl(n: number | FPInterval): FPInterval { @@ -3641,17 +3640,17 @@ export abstract class FPTraits { ) => FPInterval; private readonly LogIntervalOp: ScalarToIntervalOp = { - impl: this.limitScalarToIntervalDomain( - this.constants().greaterThanZeroInterval, - (n: number): FPInterval => { - assert(this.kind === 'f32' || this.kind === 'f16'); - const abs_error = this.kind === 'f32' ? 2 ** -21 : 2 ** -7; - if (n >= 0.5 && n <= 2.0) { - return this.absoluteErrorInterval(Math.log(n), abs_error); - } - return this.ulpInterval(Math.log(n), 3); + impl: (n: number): FPInterval => { + assert(this.kind === 'f32' || this.kind === 'f16'); + const abs_error = this.kind === 'f32' ? 2 ** -21 : 2 ** -7; + if (n >= 0.5 && n <= 2.0) { + return this.absoluteErrorInterval(Math.log(n), abs_error); } - ), + return this.ulpInterval(Math.log(n), 3); + }, + domain: () => { + return this.constants().greaterThanZeroInterval; + }, }; protected logIntervalImpl(x: number | FPInterval): FPInterval { @@ -3662,17 +3661,17 @@ export abstract class FPTraits { public abstract readonly logInterval: (x: number | FPInterval) => FPInterval; private readonly Log2IntervalOp: ScalarToIntervalOp = { - impl: this.limitScalarToIntervalDomain( - this.constants().greaterThanZeroInterval, - (n: number): FPInterval => { - assert(this.kind === 'f32' || this.kind === 'f16'); - const abs_error = this.kind === 'f32' ? 2 ** -21 : 2 ** -7; - if (n >= 0.5 && n <= 2.0) { - return this.absoluteErrorInterval(Math.log2(n), abs_error); - } - return this.ulpInterval(Math.log2(n), 3); + impl: (n: number): FPInterval => { + assert(this.kind === 'f32' || this.kind === 'f16'); + const abs_error = this.kind === 'f32' ? 2 ** -21 : 2 ** -7; + if (n >= 0.5 && n <= 2.0) { + return this.absoluteErrorInterval(Math.log2(n), abs_error); } - ), + return this.ulpInterval(Math.log2(n), 3); + }, + domain: () => { + return this.constants().greaterThanZeroInterval; + }, }; protected log2IntervalImpl(x: number | FPInterval): FPInterval { @@ -4151,14 +4150,14 @@ export abstract class FPTraits { public abstract readonly signInterval: (n: number) => FPInterval; private readonly SinIntervalOp: ScalarToIntervalOp = { - impl: this.limitScalarToIntervalDomain( - this.constants().negPiToPiInterval, - (n: number): FPInterval => { - assert(this.kind === 'f32' || this.kind === 'f16'); - const abs_error = this.kind === 'f32' ? 2 ** -11 : 2 ** -7; - return this.absoluteErrorInterval(Math.sin(n), abs_error); - } - ), + impl: (n: number): FPInterval => { + assert(this.kind === 'f32' || this.kind === 'f16'); + const abs_error = this.kind === 'f32' ? 2 ** -11 : 2 ** -7; + return this.absoluteErrorInterval(Math.sin(n), abs_error); + }, + domain: () => { + return this.constants().negPiToPiInterval; + }, }; protected sinIntervalImpl(n: number): FPInterval { @@ -4424,6 +4423,7 @@ class F32Traits extends FPTraits { kValue.f32.positive.subnormal.min, kValue.f32.positive.max ), + negOneToOneInterval: new FPInterval('f32', -1, 1), zeroVector: { 2: [kF32ZeroInterval, kF32ZeroInterval], 3: [kF32ZeroInterval, kF32ZeroInterval, kF32ZeroInterval], @@ -4890,6 +4890,8 @@ class FPAbstractTraits extends FPTraits { kValue.f64.positive.subnormal.min, kValue.f64.positive.max ), + negOneToOneInterval: new FPInterval('abstract', -1, 1), + zeroVector: { 2: [kAbstractZeroInterval, kAbstractZeroInterval], 3: [kAbstractZeroInterval, kAbstractZeroInterval, kAbstractZeroInterval], @@ -5232,6 +5234,8 @@ class F16Traits extends FPTraits { kValue.f16.positive.subnormal.min, kValue.f16.positive.max ), + negOneToOneInterval: new FPInterval('f16', -1, 1), + zeroVector: { 2: [kF16ZeroInterval, kF16ZeroInterval], 3: [kF16ZeroInterval, kF16ZeroInterval, kF16ZeroInterval],