Skip to content

Commit

Permalink
wgsl: Add execution tests for AF negation (gpuweb#2909)
Browse files Browse the repository at this point in the history
This refactors the existing code to have a clearer separation from the
non-AF test running code, and sets up for implementing vector support.

Issue gpuweb#1626
  • Loading branch information
zoddicus authored Aug 28, 2023
1 parent 5dfa3b8 commit 75c5460
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 149 deletions.
104 changes: 76 additions & 28 deletions src/unittests/floating_point.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
map2DArray,
oneULPF32,
oneULPF16,
oneULPF64,
} from '../webgpu/util/math.js';

import { UnitTest } from './unit_test.js';
Expand All @@ -37,6 +38,9 @@ const kPlusNULPFunctions = {
f16: (x: number, n: number) => {
return x + n * oneULPF16(x);
},
abstract: (x: number, n: number) => {
return x + n * oneULPF64(x);
},
};

/** @returns a number one ULP greater than the provided number */
Expand All @@ -47,6 +51,9 @@ const kPlusOneULPFunctions = {
f16: (x: number): number => {
return kPlusNULPFunctions['f16'](x, 1);
},
abstract: (x: number): number => {
return kPlusNULPFunctions['abstract'](x, 1);
},
};

/** @returns a number N * ULP less than the provided number */
Expand All @@ -57,6 +64,9 @@ const kMinusNULPFunctions = {
f16: (x: number, n: number) => {
return x - n * oneULPF16(x);
},
abstract: (x: number, n: number) => {
return x - n * oneULPF64(x);
},
};

/** @returns a number one ULP less than the provided number */
Expand All @@ -67,6 +77,9 @@ const kMinusOneULPFunctions = {
f16: (x: number): number => {
return kMinusNULPFunctions['f16'](x, 1);
},
abstract: (x: number): number => {
return kMinusNULPFunctions['abstract'](x, 1);
},
};

/** @returns the expected IntervalBounds adjusted by the given error function
Expand Down Expand Up @@ -3074,39 +3087,74 @@ g.test('log2Interval')
);
});

g.test('negationInterval_f32')
.paramsSubcasesOnly<ScalarToIntervalCase>(
// prettier-ignore
[
{ input: 0, expected: 0 },
{ input: 0.1, expected: [reinterpretU32AsF32(0xbdcccccd), kPlusOneULPFunctions['f32'](reinterpretU32AsF32(0xbdcccccd))] }, // ~-0.1
{ input: 1.0, expected: -1.0 },
{ input: 1.9, expected: [reinterpretU32AsF32(0xbff33334), kPlusOneULPFunctions['f32'](reinterpretU32AsF32(0xbff33334))] }, // ~-1.9
{ input: -0.1, expected: [kMinusOneULPFunctions['f32'](reinterpretU32AsF32(0x3dcccccd)), reinterpretU32AsF32(0x3dcccccd)] }, // ~0.1
{ input: -1.0, expected: 1 },
{ input: -1.9, expected: [kMinusOneULPFunctions['f32'](reinterpretU32AsF32(0x3ff33334)), reinterpretU32AsF32(0x3ff33334)] }, // ~1.9

// Edge cases
{ input: kValue.f32.infinity.positive, expected: kUnboundedBounds },
{ input: kValue.f32.infinity.negative, expected: kUnboundedBounds },
{ input: kValue.f32.positive.max, expected: kValue.f32.negative.min },
{ input: kValue.f32.positive.min, expected: kValue.f32.negative.max },
{ input: kValue.f32.negative.min, expected: kValue.f32.positive.max },
{ input: kValue.f32.negative.max, expected: kValue.f32.positive.min },
// prettier-ignore
const kNegationIntervalCases = {
f32: [
// Edge cases
{ input: kValue.f32.infinity.positive, expected: kUnboundedBounds },
{ input: kValue.f32.infinity.negative, expected: kUnboundedBounds },
{ input: kValue.f32.positive.max, expected: kValue.f32.negative.min },
{ input: kValue.f32.positive.min, expected: kValue.f32.negative.max },
{ input: kValue.f32.negative.min, expected: kValue.f32.positive.max },
{ input: kValue.f32.negative.max, expected: kValue.f32.positive.min },

// Normals
{ input: 0.1, expected: [reinterpretU32AsF32(0xbdcccccd), kPlusOneULPFunctions['f32'](reinterpretU32AsF32(0xbdcccccd))] }, // ~-0.1
{ input: 1.9, expected: [reinterpretU32AsF32(0xbff33334), kPlusOneULPFunctions['f32'](reinterpretU32AsF32(0xbff33334))] }, // ~-1.9
{ input: -0.1, expected: [kMinusOneULPFunctions['f32'](reinterpretU32AsF32(0x3dcccccd)), reinterpretU32AsF32(0x3dcccccd)] }, // ~0.1
{ input: -1.9, expected: [kMinusOneULPFunctions['f32'](reinterpretU32AsF32(0x3ff33334)), reinterpretU32AsF32(0x3ff33334)] }, // ~1.9

// Subnormals
{ input: kValue.f32.subnormal.positive.max, expected: [kValue.f32.subnormal.negative.min, 0] },
{ input: kValue.f32.subnormal.positive.min, expected: [kValue.f32.subnormal.negative.max, 0] },
{ input: kValue.f32.subnormal.negative.min, expected: [0, kValue.f32.subnormal.positive.max] },
{ input: kValue.f32.subnormal.negative.max, expected: [0, kValue.f32.subnormal.positive.min] },
] as ScalarToIntervalCase[],
abstract: [
// Edge cases
{ input: kValue.f64.infinity.positive, expected: kUnboundedBounds },
{ input: kValue.f64.infinity.negative, expected: kUnboundedBounds },
{ input: kValue.f64.positive.max, expected: kValue.f64.negative.min },
{ input: kValue.f64.positive.min, expected: kValue.f64.negative.max },
{ input: kValue.f64.negative.min, expected: kValue.f64.positive.max },
{ input: kValue.f64.negative.max, expected: kValue.f64.positive.min },

// Normals
{ input: 0.1, expected: -0.1 },
{ input: 1.9, expected: -1.9 },
{ input: -0.1, expected: 0.1 },
{ input: -1.9, expected: 1.9 },

// Subnormals
{ input: kValue.f64.subnormal.positive.max, expected: [kValue.f64.subnormal.negative.min, 0] },
{ input: kValue.f64.subnormal.positive.min, expected: [kValue.f64.subnormal.negative.max, 0] },
{ input: kValue.f64.subnormal.negative.min, expected: [0, kValue.f64.subnormal.positive.max] },
{ input: kValue.f64.subnormal.negative.max, expected: [0, kValue.f64.subnormal.positive.min] },
] as ScalarToIntervalCase[],
} as const;

// 32-bit subnormals
{ input: kValue.f32.subnormal.positive.max, expected: [kValue.f32.subnormal.negative.min, 0] },
{ input: kValue.f32.subnormal.positive.min, expected: [kValue.f32.subnormal.negative.max, 0] },
{ input: kValue.f32.subnormal.negative.min, expected: [0, kValue.f32.subnormal.positive.max] },
{ input: kValue.f32.subnormal.negative.max, expected: [0, kValue.f32.subnormal.positive.min] },
]
g.test('negationInterval')
.params(u =>
u
.combine('trait', ['f32', 'abstract'] as const)
.beginSubcases()
.expandWithParams<ScalarToIntervalCase>(p => {
// prettier-ignore
return [
{ input: 0, expected: 0 },
{ input: 1.0, expected: -1.0 },
{ input: -1.0, expected: 1 },
...kNegationIntervalCases[p.trait],
];
})
)
.fn(t => {
const expected = FP.f32.toInterval(t.params.expected);
const got = FP.f32.negationInterval(t.params.input);
const trait = FP[t.params.trait];
const expected = trait.toInterval(t.params.expected);
const got = trait.negationInterval(t.params.input);
t.expect(
objectEquals(expected, got),
`f32.negationInterval(${t.params.input}) returned ${got}. Expected ${expected}`
`${t.params.trait}.negationInterval(${t.params.input}) returned ${got}. Expected ${expected}`
);
});

Expand Down
Loading

0 comments on commit 75c5460

Please sign in to comment.