maths.spec.ts (100858B)
1 export const description = ` 2 Util math unit tests. 3 `; 4 5 import { makeTestGroup } from '../common/framework/test_group.js'; 6 import { objectEquals } from '../common/util/util.js'; 7 import { kBit, kValue } from '../webgpu/util/constants.js'; 8 import { 9 f16, 10 f32, 11 f64, 12 float16ToUint16, 13 float32ToUint32, 14 uint16ToFloat16, 15 uint32ToFloat32, 16 } from '../webgpu/util/conversion.js'; 17 import { 18 biasedRange, 19 calculatePermutations, 20 cartesianProduct, 21 correctlyRoundedF16, 22 correctlyRoundedF32, 23 FlushMode, 24 frexp, 25 scalarF16Range, 26 scalarF32Range, 27 fullI32Range, 28 lerp, 29 linearRange, 30 nextAfterF16, 31 nextAfterF32, 32 nextAfterF64, 33 NextDirection, 34 oneULPF16, 35 oneULPF32, 36 oneULPF64, 37 lerpBigInt, 38 linearRangeBigInt, 39 biasedRangeBigInt, 40 } from '../webgpu/util/math.js'; 41 import { 42 reinterpretU16AsF16, 43 reinterpretU32AsF32, 44 reinterpretU64AsF64, 45 } from '../webgpu/util/reinterpret.js'; 46 47 import { UnitTest } from './unit_test.js'; 48 49 export const g = makeTestGroup(UnitTest); 50 51 /** 52 * Utility wrapper around oneULP to test if a value is within 1 ULP(x) 53 * 54 * @param got number to test 55 * @param expected number to be within 1 ULP of 56 * @param mode should oneULP FTZ 57 * @returns if got is within 1 ULP of expected 58 */ 59 function withinOneULPF32(got: number, expected: number, mode: FlushMode): boolean { 60 const ulp = oneULPF32(expected, mode); 61 return got >= expected - ulp && got <= expected + ulp; 62 } 63 64 /** 65 * @returns true if arrays are equal within 1ULP, doing element-wise comparison 66 * as needed, and considering NaNs to be equal. 67 * 68 * Depends on the correctness of oneULP, which is tested in this file. 69 ** 70 * @param got array of numbers to compare for equality 71 * @param expect array of numbers to compare against 72 * @param mode should different subnormals be considered the same, i.e. should 73 * FTZ occur during comparison 74 **/ 75 function compareArrayOfNumbersF32( 76 got: readonly number[], 77 expect: readonly number[], 78 mode: FlushMode = 'flush' 79 ): boolean { 80 return ( 81 got.length === expect.length && 82 got.every((value, index) => { 83 const expected = expect[index]; 84 return ( 85 (Number.isNaN(value) && Number.isNaN(expected)) || withinOneULPF32(value, expected, mode) 86 ); 87 }) 88 ); 89 } 90 91 /** @returns the hex value representation of a f64, from is numeric representation */ 92 function float64ToUint64(value: number): bigint { 93 return new BigUint64Array(new Float64Array([value]).buffer)[0]; 94 } 95 96 /** @returns the numeric representation of a f64, from its hex value representation */ 97 function uint64ToFloat64(bits: bigint): number { 98 return new Float64Array(new BigUint64Array([bits]).buffer)[0]; 99 } 100 101 interface nextAfterCase { 102 val: number; 103 dir: NextDirection; 104 result: number; 105 } 106 107 g.test('nextAfterF64FlushToZero') 108 .paramsSubcasesOnly<nextAfterCase>( 109 // prettier-ignore 110 [ 111 // Edge Cases 112 { val: Number.NaN, dir: 'positive', result: Number.NaN }, 113 { val: Number.NaN, dir: 'negative', result: Number.NaN }, 114 { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f64.positive.infinity }, 115 { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f64.positive.infinity }, 116 { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f64.negative.infinity }, 117 { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f64.negative.infinity }, 118 119 // Zeroes 120 { val: +0, dir: 'positive', result: kValue.f64.positive.min }, 121 { val: +0, dir: 'negative', result: kValue.f64.negative.max }, 122 { val: -0, dir: 'positive', result: kValue.f64.positive.min }, 123 { val: -0, dir: 'negative', result: kValue.f64.negative.max }, 124 125 // Subnormals 126 { val: kValue.f64.positive.subnormal.min, dir: 'positive', result: kValue.f64.positive.min }, 127 { val: kValue.f64.positive.subnormal.min, dir: 'negative', result: kValue.f64.negative.max }, 128 { val: kValue.f64.positive.subnormal.max, dir: 'positive', result: kValue.f64.positive.min }, 129 { val: kValue.f64.positive.subnormal.max, dir: 'negative', result: kValue.f64.negative.max }, 130 { val: kValue.f64.negative.subnormal.min, dir: 'positive', result: kValue.f64.positive.min }, 131 { val: kValue.f64.negative.subnormal.min, dir: 'negative', result: kValue.f64.negative.max }, 132 { val: kValue.f64.negative.subnormal.max, dir: 'positive', result: kValue.f64.positive.min }, 133 { val: kValue.f64.negative.subnormal.max, dir: 'negative', result: kValue.f64.negative.max }, 134 135 // Normals 136 { val: kValue.f64.positive.max, dir: 'positive', result: kValue.f64.positive.infinity }, 137 { val: kValue.f64.positive.max, dir: 'negative', result: kValue.f64.positive.nearest_max }, 138 { val: kValue.f64.positive.min, dir: 'positive', result: reinterpretU64AsF64(0x0010_0000_0000_0001n ) }, 139 { val: kValue.f64.positive.min, dir: 'negative', result: 0 }, 140 { val: kValue.f64.negative.max, dir: 'positive', result: 0 }, 141 { val: kValue.f64.negative.max, dir: 'negative', result: reinterpretU64AsF64(0x8010_0000_0000_0001n) }, 142 { val: kValue.f64.negative.min, dir: 'positive', result: kValue.f64.negative.nearest_min }, 143 { val: kValue.f64.negative.min, dir: 'negative', result: kValue.f64.negative.infinity }, 144 { val: reinterpretU64AsF64(0x0380_0000_0000_0000n), dir: 'positive', result: reinterpretU64AsF64(0x0380_0000_0000_0001n) }, 145 { val: reinterpretU64AsF64(0x0380_0000_0000_0000n), dir: 'negative', result: reinterpretU64AsF64(0x037f_ffff_ffff_ffffn) }, 146 { val: reinterpretU64AsF64(0x8380_0000_0000_0000n), dir: 'positive', result: reinterpretU64AsF64(0x837f_ffff_ffff_ffffn) }, 147 { val: reinterpretU64AsF64(0x8380_0000_0000_0000n), dir: 'negative', result: reinterpretU64AsF64(0x8380_0000_0000_0001n) }, 148 ] 149 ) 150 .fn(t => { 151 const val = t.params.val; 152 const dir = t.params.dir; 153 const expect = t.params.result; 154 const got = nextAfterF64(val, dir, 'flush'); 155 t.expect( 156 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 157 `nextAfterF64(${f64(val)}, '${dir}', 'flush') returned ${f64(got)}. Expected ${f64(expect)}` 158 ); 159 }); 160 161 g.test('nextAfterF64NoFlush') 162 .paramsSubcasesOnly<nextAfterCase>( 163 // prettier-ignore 164 [ 165 // Edge Cases 166 { val: Number.NaN, dir: 'positive', result: Number.NaN }, 167 { val: Number.NaN, dir: 'negative', result: Number.NaN }, 168 { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f64.positive.infinity }, 169 { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f64.positive.infinity }, 170 { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f64.negative.infinity }, 171 { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f64.negative.infinity }, 172 173 // Zeroes 174 { val: +0, dir: 'positive', result: kValue.f64.positive.subnormal.min }, 175 { val: +0, dir: 'negative', result: kValue.f64.negative.subnormal.max }, 176 { val: -0, dir: 'positive', result: kValue.f64.positive.subnormal.min }, 177 { val: -0, dir: 'negative', result: kValue.f64.negative.subnormal.max }, 178 179 // Subnormals 180 { val: kValue.f64.positive.subnormal.min, dir: 'positive', result: reinterpretU64AsF64(0x0000_0000_0000_0002n) }, 181 { val: kValue.f64.positive.subnormal.min, dir: 'negative', result: 0 }, 182 { val: kValue.f64.positive.subnormal.max, dir: 'positive', result: kValue.f64.positive.min }, 183 { val: kValue.f64.positive.subnormal.max, dir: 'negative', result: reinterpretU64AsF64(0x000f_ffff_ffff_fffen) }, 184 { val: kValue.f64.negative.subnormal.min, dir: 'positive', result: reinterpretU64AsF64(0x800f_ffff_ffff_fffen) }, 185 { val: kValue.f64.negative.subnormal.min, dir: 'negative', result: kValue.f64.negative.max }, 186 { val: kValue.f64.negative.subnormal.max, dir: 'positive', result: 0 }, 187 { val: kValue.f64.negative.subnormal.max, dir: 'negative', result: reinterpretU64AsF64(0x8000_0000_0000_0002n) }, 188 189 // Normals 190 { val: kValue.f64.positive.max, dir: 'positive', result: kValue.f64.positive.infinity }, 191 { val: kValue.f64.positive.max, dir: 'negative', result: kValue.f64.positive.nearest_max }, 192 { val: kValue.f64.positive.min, dir: 'positive', result: reinterpretU64AsF64(0x0010_0000_0000_0001n ) }, 193 { val: kValue.f64.positive.min, dir: 'negative', result: reinterpretU64AsF64(0x000f_ffff_ffff_ffffn) }, 194 { val: kValue.f64.negative.max, dir: 'positive', result: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn) }, 195 { val: kValue.f64.negative.max, dir: 'negative', result: reinterpretU64AsF64(0x8010_0000_0000_0001n) }, 196 { val: kValue.f64.negative.min, dir: 'positive', result: kValue.f64.negative.nearest_min }, 197 { val: kValue.f64.negative.min, dir: 'negative', result: kValue.f64.negative.infinity }, 198 { val: reinterpretU64AsF64(0x0380_0000_0000_0000n), dir: 'positive', result: reinterpretU64AsF64(0x0380_0000_0000_0001n) }, 199 { val: reinterpretU64AsF64(0x0380_0000_0000_0000n), dir: 'negative', result: reinterpretU64AsF64(0x037f_ffff_ffff_ffffn) }, 200 { val: reinterpretU64AsF64(0x8380_0000_0000_0000n), dir: 'positive', result: reinterpretU64AsF64(0x837f_ffff_ffff_ffffn) }, 201 { val: reinterpretU64AsF64(0x8380_0000_0000_0000n), dir: 'negative', result: reinterpretU64AsF64(0x8380_0000_0000_0001n) }, 202 ] 203 ) 204 .fn(t => { 205 const val = t.params.val; 206 const dir = t.params.dir; 207 const expect = t.params.result; 208 const got = nextAfterF64(val, dir, 'no-flush'); 209 t.expect( 210 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 211 `nextAfterF64(${f64(val)}, '${dir}', 'no-flush') returned ${f64(got)}. Expected ${f64( 212 expect 213 )}` 214 ); 215 }); 216 217 g.test('nextAfterF32FlushToZero') 218 .paramsSubcasesOnly<nextAfterCase>( 219 // prettier-ignore 220 [ 221 // Edge Cases 222 { val: Number.NaN, dir: 'positive', result: Number.NaN }, 223 { val: Number.NaN, dir: 'negative', result: Number.NaN }, 224 { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f32.positive.infinity }, 225 { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f32.positive.infinity }, 226 { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f32.negative.infinity }, 227 { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f32.negative.infinity }, 228 229 // Zeroes 230 { val: +0, dir: 'positive', result: kValue.f32.positive.min }, 231 { val: +0, dir: 'negative', result: kValue.f32.negative.max }, 232 { val: -0, dir: 'positive', result: kValue.f32.positive.min }, 233 { val: -0, dir: 'negative', result: kValue.f32.negative.max }, 234 235 // Subnormals 236 { val: kValue.f32.positive.subnormal.min, dir: 'positive', result: kValue.f32.positive.min }, 237 { val: kValue.f32.positive.subnormal.min, dir: 'negative', result: kValue.f32.negative.max }, 238 { val: kValue.f32.positive.subnormal.max, dir: 'positive', result: kValue.f32.positive.min }, 239 { val: kValue.f32.positive.subnormal.max, dir: 'negative', result: kValue.f32.negative.max }, 240 { val: kValue.f32.negative.subnormal.min, dir: 'positive', result: kValue.f32.positive.min }, 241 { val: kValue.f32.negative.subnormal.min, dir: 'negative', result: kValue.f32.negative.max }, 242 { val: kValue.f32.negative.subnormal.max, dir: 'positive', result: kValue.f32.positive.min }, 243 { val: kValue.f32.negative.subnormal.max, dir: 'negative', result: kValue.f32.negative.max }, 244 245 // Normals 246 { val: kValue.f32.positive.max, dir: 'positive', result: kValue.f32.positive.infinity }, 247 { val: kValue.f32.positive.max, dir: 'negative', result: kValue.f32.positive.nearest_max }, 248 { val: kValue.f32.positive.min, dir: 'positive', result: reinterpretU32AsF32(0x00800001) }, 249 { val: kValue.f32.positive.min, dir: 'negative', result: 0 }, 250 { val: kValue.f32.negative.max, dir: 'positive', result: 0 }, 251 { val: kValue.f32.negative.max, dir: 'negative', result: reinterpretU32AsF32(0x80800001) }, 252 { val: kValue.f32.negative.min, dir: 'positive', result: reinterpretU32AsF32(0xff7ffffe) }, 253 { val: kValue.f32.negative.min, dir: 'negative', result: kValue.f32.negative.infinity }, 254 { val: reinterpretU32AsF32(0x03800000), dir: 'positive', result: reinterpretU32AsF32(0x03800001) }, 255 { val: reinterpretU32AsF32(0x03800000), dir: 'negative', result: reinterpretU32AsF32(0x037fffff) }, 256 { val: reinterpretU32AsF32(0x83800000), dir: 'positive', result: reinterpretU32AsF32(0x837fffff) }, 257 { val: reinterpretU32AsF32(0x83800000), dir: 'negative', result: reinterpretU32AsF32(0x83800001) }, 258 259 // Not precisely expressible as f32 260 { val: 0.001, dir: 'positive', result: reinterpretU32AsF32(0x3a83126f) }, // positive normal 261 { val: 0.001, dir: 'negative', result: reinterpretU32AsF32(0x3a83126e) }, // positive normal 262 { val: -0.001, dir: 'positive', result: reinterpretU32AsF32(0xba83126e) }, // negative normal 263 { val: -0.001, dir: 'negative', result: reinterpretU32AsF32(0xba83126f) }, // negative normal 264 { val: 2.82E-40, dir: 'positive', result: kValue.f32.positive.min }, // positive subnormal 265 { val: 2.82E-40, dir: 'negative', result: kValue.f32.negative.max }, // positive subnormal 266 { val: -2.82E-40, dir: 'positive', result: kValue.f32.positive.min }, // negative subnormal 267 { val: -2.82E-40, dir: 'negative', result: kValue.f32.negative.max }, // negative subnormal 268 ] 269 ) 270 .fn(t => { 271 const val = t.params.val; 272 const dir = t.params.dir; 273 const expect = t.params.result; 274 const got = nextAfterF32(val, dir, 'flush'); 275 t.expect( 276 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 277 `nextAfterF32(${f64(val)}, '${dir}', 'flush') returned ${f32(got)}. Expected ${f32(expect)}` 278 ); 279 }); 280 281 g.test('nextAfterF32NoFlush') 282 .paramsSubcasesOnly<nextAfterCase>( 283 // prettier-ignore 284 [ 285 // Edge Cases 286 { val: Number.NaN, dir: 'positive', result: Number.NaN }, 287 { val: Number.NaN, dir: 'negative', result: Number.NaN }, 288 { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f32.positive.infinity }, 289 { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f32.positive.infinity }, 290 { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f32.negative.infinity }, 291 { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f32.negative.infinity }, 292 293 // Zeroes 294 { val: +0, dir: 'positive', result: kValue.f32.positive.subnormal.min }, 295 { val: +0, dir: 'negative', result: kValue.f32.negative.subnormal.max }, 296 { val: -0, dir: 'positive', result: kValue.f32.positive.subnormal.min }, 297 { val: -0, dir: 'negative', result: kValue.f32.negative.subnormal.max }, 298 299 // Subnormals 300 { val:kValue.f32.positive.subnormal.min, dir: 'positive', result: reinterpretU32AsF32(0x00000002) }, 301 { val:kValue.f32.positive.subnormal.min, dir: 'negative', result: 0 }, 302 { val:kValue.f32.positive.subnormal.max, dir: 'positive', result: kValue.f32.positive.min }, 303 { val:kValue.f32.positive.subnormal.max, dir: 'negative', result: reinterpretU32AsF32(0x007ffffe) }, 304 { val:kValue.f32.negative.subnormal.min, dir: 'positive', result: reinterpretU32AsF32(0x807ffffe) }, 305 { val:kValue.f32.negative.subnormal.min, dir: 'negative', result: kValue.f32.negative.max }, 306 { val:kValue.f32.negative.subnormal.max, dir: 'positive', result: 0 }, 307 { val:kValue.f32.negative.subnormal.max, dir: 'negative', result: reinterpretU32AsF32(0x80000002) }, 308 309 // Normals 310 { val: kValue.f32.positive.max, dir: 'positive', result: kValue.f32.positive.infinity }, 311 { val: kValue.f32.positive.max, dir: 'negative', result: kValue.f32.positive.nearest_max }, 312 { val: kValue.f32.positive.min, dir: 'positive', result: reinterpretU32AsF32(0x00800001) }, 313 { val: kValue.f32.positive.min, dir: 'negative', result: kValue.f32.positive.subnormal.max }, 314 { val: kValue.f32.negative.max, dir: 'positive', result: kValue.f32.negative.subnormal.min }, 315 { val: kValue.f32.negative.max, dir: 'negative', result: reinterpretU32AsF32(0x80800001) }, 316 { val: kValue.f32.negative.min, dir: 'positive', result: kValue.f32.negative.nearest_min }, 317 { val: kValue.f32.negative.min, dir: 'negative', result: kValue.f32.negative.infinity }, 318 { val: reinterpretU32AsF32(0x03800000), dir: 'positive', result: reinterpretU32AsF32(0x03800001) }, 319 { val: reinterpretU32AsF32(0x03800000), dir: 'negative', result: reinterpretU32AsF32(0x037fffff) }, 320 { val: reinterpretU32AsF32(0x83800000), dir: 'positive', result: reinterpretU32AsF32(0x837fffff) }, 321 { val: reinterpretU32AsF32(0x83800000), dir: 'negative', result: reinterpretU32AsF32(0x83800001) }, 322 323 // Not precisely expressible as f32 324 { val: 0.001, dir: 'positive', result: reinterpretU32AsF32(0x3a83126f) }, // positive normal 325 { val: 0.001, dir: 'negative', result: reinterpretU32AsF32(0x3a83126e) }, // positive normal 326 { val: -0.001, dir: 'positive', result: reinterpretU32AsF32(0xba83126e) }, // negative normal 327 { val: -0.001, dir: 'negative', result: reinterpretU32AsF32(0xba83126f) }, // negative normal 328 { val: 2.82E-40, dir: 'positive', result: reinterpretU32AsF32(0x0003121a) }, // positive subnormal 329 { val: 2.82E-40, dir: 'negative', result: reinterpretU32AsF32(0x00031219) }, // positive subnormal 330 { val: -2.82E-40, dir: 'positive', result: reinterpretU32AsF32(0x80031219) }, // negative subnormal 331 { val: -2.82E-40, dir: 'negative', result: reinterpretU32AsF32(0x8003121a) }, // negative subnormal 332 ] 333 ) 334 .fn(t => { 335 const val = t.params.val; 336 const dir = t.params.dir; 337 const expect = t.params.result; 338 const got = nextAfterF32(val, dir, 'no-flush'); 339 t.expect( 340 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 341 `nextAfterF32(${f64(val)}, '${dir}', 'no-flush') returned ${f32(got)}. Expected ${f32( 342 expect 343 )}` 344 ); 345 }); 346 347 g.test('nextAfterF16FlushToZero') 348 .paramsSubcasesOnly<nextAfterCase>( 349 // prettier-ignore 350 [ 351 // Edge Cases 352 { val: Number.NaN, dir: 'positive', result: Number.NaN }, 353 { val: Number.NaN, dir: 'negative', result: Number.NaN }, 354 { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f16.positive.infinity }, 355 { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f16.positive.infinity }, 356 { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f16.negative.infinity }, 357 { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f16.negative.infinity }, 358 359 // Zeroes 360 { val: +0, dir: 'positive', result: kValue.f16.positive.min }, 361 { val: +0, dir: 'negative', result: kValue.f16.negative.max }, 362 { val: -0, dir: 'positive', result: kValue.f16.positive.min }, 363 { val: -0, dir: 'negative', result: kValue.f16.negative.max }, 364 365 // Subnormals 366 { val: kValue.f16.positive.subnormal.min, dir: 'positive', result: kValue.f16.positive.min }, 367 { val: kValue.f16.positive.subnormal.min, dir: 'negative', result: kValue.f16.negative.max }, 368 { val: kValue.f16.positive.subnormal.max, dir: 'positive', result: kValue.f16.positive.min }, 369 { val: kValue.f16.positive.subnormal.max, dir: 'negative', result: kValue.f16.negative.max }, 370 { val: kValue.f16.negative.subnormal.min, dir: 'positive', result: kValue.f16.positive.min }, 371 { val: kValue.f16.negative.subnormal.min, dir: 'negative', result: kValue.f16.negative.max }, 372 { val: kValue.f16.negative.subnormal.max, dir: 'positive', result: kValue.f16.positive.min }, 373 { val: kValue.f16.negative.subnormal.max, dir: 'negative', result: kValue.f16.negative.max }, 374 375 // Normals 376 { val: kValue.f16.positive.max, dir: 'positive', result: kValue.f16.positive.infinity }, 377 { val: kValue.f16.positive.max, dir: 'negative', result: reinterpretU16AsF16(0x7bfe) }, 378 { val: kValue.f16.positive.min, dir: 'positive', result: reinterpretU16AsF16(0x0401) }, 379 { val: kValue.f16.positive.min, dir: 'negative', result: 0 }, 380 { val: kValue.f16.negative.max, dir: 'positive', result: 0 }, 381 { val: kValue.f16.negative.max, dir: 'negative', result: reinterpretU16AsF16(0x8401) }, 382 { val: kValue.f16.negative.min, dir: 'positive', result: reinterpretU16AsF16(0xfbfe) }, 383 { val: kValue.f16.negative.min, dir: 'negative', result: kValue.f16.negative.infinity }, 384 { val: reinterpretU16AsF16(0x1380), dir: 'positive', result: reinterpretU16AsF16(0x1381) }, 385 { val: reinterpretU16AsF16(0x1380), dir: 'negative', result: reinterpretU16AsF16(0x137f) }, 386 { val: reinterpretU16AsF16(0x9380), dir: 'positive', result: reinterpretU16AsF16(0x937f) }, 387 { val: reinterpretU16AsF16(0x9380), dir: 'negative', result: reinterpretU16AsF16(0x9381) }, 388 389 // Not precisely expressible as f16 390 { val: 0.01, dir: 'positive', result: reinterpretU16AsF16(0x211f) }, // positive normal 391 { val: 0.01, dir: 'negative', result: reinterpretU16AsF16(0x211e) }, // positive normal 392 { val: -0.01, dir: 'positive', result: reinterpretU16AsF16(0xa11e) }, // negative normal 393 { val: -0.01, dir: 'negative', result: reinterpretU16AsF16(0xa11f) }, // negative normal 394 { val: 2.82E-40, dir: 'positive', result: kValue.f16.positive.min }, // positive subnormal 395 { val: 2.82E-40, dir: 'negative', result: kValue.f16.negative.max }, // positive subnormal 396 { val: -2.82E-40, dir: 'positive', result: kValue.f16.positive.min }, // negative subnormal 397 { val: -2.82E-40, dir: 'negative', result: kValue.f16.negative.max }, // negative subnormal 398 ] 399 ) 400 .fn(t => { 401 const val = t.params.val; 402 const dir = t.params.dir; 403 const expect = t.params.result; 404 const got = nextAfterF16(val, dir, 'flush'); 405 t.expect( 406 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 407 `nextAfterF16(${f64(val)}, '${dir}', 'flush') returned ${f16(got)}. Expected ${f16(expect)}` 408 ); 409 }); 410 411 g.test('nextAfterF16NoFlush') 412 .paramsSubcasesOnly<nextAfterCase>( 413 // prettier-ignore 414 [ 415 // Edge Cases 416 { val: Number.NaN, dir: 'positive', result: Number.NaN }, 417 { val: Number.NaN, dir: 'negative', result: Number.NaN }, 418 { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f16.positive.infinity }, 419 { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f16.positive.infinity }, 420 { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f16.negative.infinity }, 421 { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f16.negative.infinity }, 422 423 // Zeroes 424 { val: +0, dir: 'positive', result: kValue.f16.positive.subnormal.min }, 425 { val: +0, dir: 'negative', result: kValue.f16.negative.subnormal.max }, 426 { val: -0, dir: 'positive', result: kValue.f16.positive.subnormal.min }, 427 { val: -0, dir: 'negative', result: kValue.f16.negative.subnormal.max }, 428 429 // Subnormals 430 { val: kValue.f16.positive.subnormal.min, dir: 'positive', result: reinterpretU16AsF16(0x0002) }, 431 { val: kValue.f16.positive.subnormal.min, dir: 'negative', result: 0 }, 432 { val: kValue.f16.positive.subnormal.max, dir: 'positive', result: kValue.f16.positive.min }, 433 { val: kValue.f16.positive.subnormal.max, dir: 'negative', result: reinterpretU16AsF16(0x03fe) }, 434 { val: kValue.f16.negative.subnormal.min, dir: 'positive', result: reinterpretU16AsF16(0x83fe) }, 435 { val: kValue.f16.negative.subnormal.min, dir: 'negative', result: kValue.f16.negative.max }, 436 { val: kValue.f16.negative.subnormal.max, dir: 'positive', result: 0 }, 437 { val: kValue.f16.negative.subnormal.max, dir: 'negative', result: reinterpretU16AsF16(0x8002) }, 438 439 // Normals 440 { val: kValue.f16.positive.max, dir: 'positive', result: kValue.f16.positive.infinity }, 441 { val: kValue.f16.positive.max, dir: 'negative', result: reinterpretU16AsF16(0x7bfe) }, 442 { val: kValue.f16.positive.min, dir: 'positive', result: reinterpretU16AsF16(0x0401) }, 443 { val: kValue.f16.positive.min, dir: 'negative', result: kValue.f16.positive.subnormal.max }, 444 { val: kValue.f16.negative.max, dir: 'positive', result: kValue.f16.negative.subnormal.min }, 445 { val: kValue.f16.negative.max, dir: 'negative', result: reinterpretU16AsF16(0x8401) }, 446 { val: kValue.f16.negative.min, dir: 'positive', result: reinterpretU16AsF16(0xfbfe) }, 447 { val: kValue.f16.negative.min, dir: 'negative', result: kValue.f16.negative.infinity }, 448 { val: reinterpretU16AsF16(0x1380), dir: 'positive', result: reinterpretU16AsF16(0x1381) }, 449 { val: reinterpretU16AsF16(0x1380), dir: 'negative', result: reinterpretU16AsF16(0x137f) }, 450 { val: reinterpretU16AsF16(0x9380), dir: 'positive', result: reinterpretU16AsF16(0x937f) }, 451 { val: reinterpretU16AsF16(0x9380), dir: 'negative', result: reinterpretU16AsF16(0x9381) }, 452 453 // Not precisely expressible as f16 454 { val: 0.01, dir: 'positive', result: reinterpretU16AsF16(0x211f) }, // positive normal 455 { val: 0.01, dir: 'negative', result: reinterpretU16AsF16(0x211e) }, // positive normal 456 { val: -0.01, dir: 'positive', result: reinterpretU16AsF16(0xa11e) }, // negative normal 457 { val: -0.01, dir: 'negative', result: reinterpretU16AsF16(0xa11f) }, // negative normal 458 { val: 2.82E-40, dir: 'positive', result: kValue.f16.positive.subnormal.min }, // positive subnormal 459 { val: 2.82E-40, dir: 'negative', result: 0 }, // positive subnormal 460 { val: -2.82E-40, dir: 'positive', result: 0 }, // negative subnormal 461 { val: -2.82E-40, dir: 'negative', result: kValue.f16.negative.subnormal.max }, // negative subnormal 462 ] 463 ) 464 .fn(t => { 465 const val = t.params.val; 466 const dir = t.params.dir; 467 const expect = t.params.result; 468 const got = nextAfterF16(val, dir, 'no-flush'); 469 t.expect( 470 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 471 `nextAfterF16(${f64(val)}, '${dir}', 'no-flush') returned ${f16(got)}. Expected ${f16( 472 expect 473 )}` 474 ); 475 }); 476 477 interface OneULPCase { 478 target: number; 479 expect: number; 480 } 481 482 g.test('oneULPF64FlushToZero') 483 .paramsSimple<OneULPCase>([ 484 // Edge Cases 485 { target: Number.NaN, expect: Number.NaN }, 486 { target: Number.POSITIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 487 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 488 489 // Zeroes 490 { target: +0, expect: reinterpretU64AsF64(0x0010_0000_0000_0000n) }, 491 { target: -0, expect: reinterpretU64AsF64(0x0010_0000_0000_0000n) }, 492 493 // Subnormals 494 { 495 target: kValue.f64.positive.subnormal.min, 496 expect: reinterpretU64AsF64(0x0010_0000_0000_0000n), 497 }, 498 { 499 target: kValue.f64.positive.subnormal.max, 500 expect: reinterpretU64AsF64(0x0010_0000_0000_0000n), 501 }, 502 { 503 target: kValue.f64.negative.subnormal.min, 504 expect: reinterpretU64AsF64(0x0010_0000_0000_0000n), 505 }, 506 { 507 target: kValue.f64.negative.subnormal.max, 508 expect: reinterpretU64AsF64(0x0010_0000_0000_0000n), 509 }, 510 511 // Normals 512 { target: kValue.f64.positive.min, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) }, 513 { target: 1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) }, 514 { target: 2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) }, 515 { target: 4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) }, 516 { target: 1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) }, 517 { target: kValue.f64.positive.max, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 518 { target: kValue.f64.negative.max, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) }, 519 { target: -1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) }, 520 { target: -2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) }, 521 { target: -4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) }, 522 { target: -1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) }, 523 { target: kValue.f64.negative.min, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 524 ]) 525 .fn(t => { 526 const target = t.params.target; 527 const got = oneULPF64(target, 'flush'); 528 const expect = t.params.expect; 529 t.expect( 530 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 531 `oneULPF64(${f64(target)}, 'flush') returned ${f64(got)}. Expected ${f64(expect)}` 532 ); 533 }); 534 535 g.test('oneULPF64NoFlush') 536 .paramsSimple<OneULPCase>([ 537 // Edge Cases 538 { target: Number.NaN, expect: Number.NaN }, 539 { target: Number.POSITIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 540 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 541 542 // Zeroes 543 { target: +0, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) }, 544 { target: -0, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) }, 545 546 // Subnormals 547 { 548 target: kValue.f64.positive.subnormal.min, 549 expect: reinterpretU64AsF64(0x0000_0000_0000_0001n), 550 }, 551 { 552 target: kValue.f64.positive.subnormal.max, 553 expect: reinterpretU64AsF64(0x0000_0000_0000_0001n), 554 }, 555 { 556 target: kValue.f64.negative.subnormal.min, 557 expect: reinterpretU64AsF64(0x0000_0000_0000_0001n), 558 }, 559 { 560 target: kValue.f64.negative.subnormal.max, 561 expect: reinterpretU64AsF64(0x0000_0000_0000_0001n), 562 }, 563 564 // Normals 565 { target: kValue.f64.positive.min, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) }, 566 { target: 1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) }, 567 { target: 2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) }, 568 { target: 4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) }, 569 { target: 1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) }, 570 { target: kValue.f64.positive.max, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 571 { target: kValue.f64.negative.max, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) }, 572 { target: -1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) }, 573 { target: -2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) }, 574 { target: -4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) }, 575 { target: -1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) }, 576 { target: kValue.f64.negative.min, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 577 ]) 578 .fn(t => { 579 const target = t.params.target; 580 const got = oneULPF64(target, 'no-flush'); 581 const expect = t.params.expect; 582 t.expect( 583 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 584 `oneULPF64(${f64(target)}, 'no-flush') returned ${f64(got)}. Expected ${f64(expect)}` 585 ); 586 }); 587 588 g.test('oneULPF64') 589 .paramsSimple<OneULPCase>([ 590 // Edge Cases 591 { target: Number.NaN, expect: Number.NaN }, 592 { target: Number.POSITIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 593 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 594 595 // Zeroes 596 { target: +0, expect: reinterpretU64AsF64(0x0010_0000_0000_0000n) }, 597 { target: -0, expect: reinterpretU64AsF64(0x0010_0000_0000_0000n) }, 598 599 // Subnormals 600 { 601 target: kValue.f64.positive.subnormal.min, 602 expect: reinterpretU64AsF64(0x0010_0000_0000_0000n), 603 }, 604 { 605 target: kValue.f64.positive.subnormal.max, 606 expect: reinterpretU64AsF64(0x0010_0000_0000_0000n), 607 }, 608 { 609 target: kValue.f64.negative.subnormal.min, 610 expect: reinterpretU64AsF64(0x0010_0000_0000_0000n), 611 }, 612 { 613 target: kValue.f64.negative.subnormal.max, 614 expect: reinterpretU64AsF64(0x0010_0000_0000_0000n), 615 }, 616 617 // Normals 618 { target: kValue.f64.positive.min, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) }, 619 { target: 1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) }, 620 { target: 2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) }, 621 { target: 4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) }, 622 { target: 1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) }, 623 { target: kValue.f64.positive.max, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 624 { target: kValue.f64.negative.max, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) }, 625 { target: -1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) }, 626 { target: -2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) }, 627 { target: -4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) }, 628 { target: -1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) }, 629 { target: kValue.f64.negative.min, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) }, 630 ]) 631 .fn(t => { 632 const target = t.params.target; 633 const got = oneULPF64(target); 634 const expect = t.params.expect; 635 t.expect( 636 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 637 `oneULPF64(${f64(target)}) returned ${f64(got)}. Expected ${f64(expect)}` 638 ); 639 }); 640 641 g.test('oneULPF32FlushToZero') 642 .paramsSimple<OneULPCase>([ 643 // Edge Cases 644 { target: Number.NaN, expect: Number.NaN }, 645 { target: Number.POSITIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) }, 646 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) }, 647 648 // Zeroes 649 { target: +0, expect: reinterpretU32AsF32(0x00800000) }, 650 { target: -0, expect: reinterpretU32AsF32(0x00800000) }, 651 652 // Subnormals 653 { target: kValue.f32.positive.subnormal.min, expect: reinterpretU32AsF32(0x00800000) }, 654 { target: 2.82e-40, expect: reinterpretU32AsF32(0x00800000) }, // positive subnormal 655 { target: kValue.f32.positive.subnormal.max, expect: reinterpretU32AsF32(0x00800000) }, 656 { target: kValue.f32.negative.subnormal.min, expect: reinterpretU32AsF32(0x00800000) }, 657 { target: -2.82e-40, expect: reinterpretU32AsF32(0x00800000) }, // negative subnormal 658 { target: kValue.f32.negative.subnormal.max, expect: reinterpretU32AsF32(0x00800000) }, 659 660 // Normals 661 { target: kValue.f32.positive.min, expect: reinterpretU32AsF32(0x00000001) }, 662 { target: 1, expect: reinterpretU32AsF32(0x33800000) }, 663 { target: 2, expect: reinterpretU32AsF32(0x34000000) }, 664 { target: 4, expect: reinterpretU32AsF32(0x34800000) }, 665 { target: 1000000, expect: reinterpretU32AsF32(0x3d800000) }, 666 { target: kValue.f32.positive.max, expect: reinterpretU32AsF32(0x73800000) }, 667 { target: kValue.f32.negative.max, expect: reinterpretU32AsF32(0x00000001) }, 668 { target: -1, expect: reinterpretU32AsF32(0x33800000) }, 669 { target: -2, expect: reinterpretU32AsF32(0x34000000) }, 670 { target: -4, expect: reinterpretU32AsF32(0x34800000) }, 671 { target: -1000000, expect: reinterpretU32AsF32(0x3d800000) }, 672 { target: kValue.f32.negative.min, expect: reinterpretU32AsF32(0x73800000) }, 673 674 // No precise f32 value 675 { target: 0.001, expect: reinterpretU32AsF32(0x2f000000) }, // positive normal 676 { target: -0.001, expect: reinterpretU32AsF32(0x2f000000) }, // negative normal 677 { target: 1e40, expect: reinterpretU32AsF32(0x73800000) }, // positive out of range 678 { target: -1e40, expect: reinterpretU32AsF32(0x73800000) }, // negative out of range 679 ]) 680 .fn(t => { 681 const target = t.params.target; 682 const got = oneULPF32(target, 'flush'); 683 const expect = t.params.expect; 684 t.expect( 685 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 686 `oneULPF32(${target}, 'flush') returned ${got}. Expected ${expect}` 687 ); 688 }); 689 690 g.test('oneULPF32NoFlush') 691 .paramsSimple<OneULPCase>([ 692 // Edge Cases 693 { target: Number.NaN, expect: Number.NaN }, 694 { target: Number.POSITIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) }, 695 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) }, 696 697 // Zeroes 698 { target: +0, expect: reinterpretU32AsF32(0x00000001) }, 699 { target: -0, expect: reinterpretU32AsF32(0x00000001) }, 700 701 // Subnormals 702 { target: kValue.f32.positive.subnormal.min, expect: reinterpretU32AsF32(0x00000001) }, 703 { target: -2.82e-40, expect: reinterpretU32AsF32(0x00000001) }, // negative subnormal 704 { target: kValue.f32.positive.subnormal.max, expect: reinterpretU32AsF32(0x00000001) }, 705 { target: kValue.f32.negative.subnormal.min, expect: reinterpretU32AsF32(0x00000001) }, 706 { target: 2.82e-40, expect: reinterpretU32AsF32(0x00000001) }, // positive subnormal 707 { target: kValue.f32.negative.subnormal.max, expect: reinterpretU32AsF32(0x00000001) }, 708 709 // Normals 710 { target: kValue.f32.positive.min, expect: reinterpretU32AsF32(0x00000001) }, 711 { target: 1, expect: reinterpretU32AsF32(0x33800000) }, 712 { target: 2, expect: reinterpretU32AsF32(0x34000000) }, 713 { target: 4, expect: reinterpretU32AsF32(0x34800000) }, 714 { target: 1000000, expect: reinterpretU32AsF32(0x3d800000) }, 715 { target: kValue.f32.positive.max, expect: reinterpretU32AsF32(0x73800000) }, 716 { target: kValue.f32.negative.max, expect: reinterpretU32AsF32(0x00000001) }, 717 { target: -1, expect: reinterpretU32AsF32(0x33800000) }, 718 { target: -2, expect: reinterpretU32AsF32(0x34000000) }, 719 { target: -4, expect: reinterpretU32AsF32(0x34800000) }, 720 { target: -1000000, expect: reinterpretU32AsF32(0x3d800000) }, 721 { target: kValue.f32.negative.min, expect: reinterpretU32AsF32(0x73800000) }, 722 723 // No precise f32 value 724 { target: 0.001, expect: reinterpretU32AsF32(0x2f000000) }, // positive normal 725 { target: -0.001, expect: reinterpretU32AsF32(0x2f000000) }, // negative normal 726 { target: 1e40, expect: reinterpretU32AsF32(0x73800000) }, // positive out of range 727 { target: -1e40, expect: reinterpretU32AsF32(0x73800000) }, // negative out of range 728 ]) 729 .fn(t => { 730 const target = t.params.target; 731 const got = oneULPF32(target, 'no-flush'); 732 const expect = t.params.expect; 733 t.expect( 734 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 735 `oneULPF32(${target}, no-flush) returned ${got}. Expected ${expect}` 736 ); 737 }); 738 739 g.test('oneULPF32') 740 .paramsSimple<OneULPCase>([ 741 // Edge Cases 742 { target: Number.NaN, expect: Number.NaN }, 743 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) }, 744 { target: Number.POSITIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) }, 745 746 // Zeroes 747 { target: +0, expect: reinterpretU32AsF32(0x00800000) }, 748 { target: -0, expect: reinterpretU32AsF32(0x00800000) }, 749 750 // Subnormals 751 { target: kValue.f32.negative.subnormal.max, expect: reinterpretU32AsF32(0x00800000) }, 752 { target: -2.82e-40, expect: reinterpretU32AsF32(0x00800000) }, 753 { target: kValue.f32.negative.subnormal.min, expect: reinterpretU32AsF32(0x00800000) }, 754 { target: kValue.f32.positive.subnormal.max, expect: reinterpretU32AsF32(0x00800000) }, 755 { target: 2.82e-40, expect: reinterpretU32AsF32(0x00800000) }, 756 { target: kValue.f32.positive.subnormal.min, expect: reinterpretU32AsF32(0x00800000) }, 757 758 // Normals 759 { target: kValue.f32.positive.min, expect: reinterpretU32AsF32(0x00000001) }, 760 { target: 1, expect: reinterpretU32AsF32(0x33800000) }, 761 { target: 2, expect: reinterpretU32AsF32(0x34000000) }, 762 { target: 4, expect: reinterpretU32AsF32(0x34800000) }, 763 { target: 1000000, expect: reinterpretU32AsF32(0x3d800000) }, 764 { target: kValue.f32.positive.max, expect: reinterpretU32AsF32(0x73800000) }, 765 { target: kValue.f32.negative.max, expect: reinterpretU32AsF32(0x000000001) }, 766 { target: -1, expect: reinterpretU32AsF32(0x33800000) }, 767 { target: -2, expect: reinterpretU32AsF32(0x34000000) }, 768 { target: -4, expect: reinterpretU32AsF32(0x34800000) }, 769 { target: -1000000, expect: reinterpretU32AsF32(0x3d800000) }, 770 { target: kValue.f32.negative.min, expect: reinterpretU32AsF32(0x73800000) }, 771 772 // No precise f32 value 773 { target: -0.001, expect: reinterpretU32AsF32(0x2f000000) }, // negative normal 774 { target: -1e40, expect: reinterpretU32AsF32(0x73800000) }, // negative out of range 775 { target: 0.001, expect: reinterpretU32AsF32(0x2f000000) }, // positive normal 776 { target: 1e40, expect: reinterpretU32AsF32(0x73800000) }, // positive out of range 777 ]) 778 .fn(t => { 779 const target = t.params.target; 780 const got = oneULPF32(target); 781 const expect = t.params.expect; 782 t.expect( 783 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 784 `oneULPF32(${target}) returned ${got}. Expected ${expect}` 785 ); 786 }); 787 788 g.test('oneULPF16FlushToZero') 789 .paramsSubcasesOnly<OneULPCase>([ 790 // Edge Cases 791 { target: Number.NaN, expect: Number.NaN }, 792 { target: Number.POSITIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) }, 793 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) }, 794 795 // Zeroes, expect positive.min in flush mode 796 { target: +0, expect: reinterpretU16AsF16(0x0400) }, 797 { target: -0, expect: reinterpretU16AsF16(0x0400) }, 798 799 // Subnormals 800 { target: kValue.f16.positive.subnormal.min, expect: reinterpretU16AsF16(0x0400) }, 801 { target: 1.91e-6, expect: reinterpretU16AsF16(0x0400) }, // positive subnormal 802 { target: kValue.f16.positive.subnormal.max, expect: reinterpretU16AsF16(0x0400) }, 803 { target: kValue.f16.negative.subnormal.min, expect: reinterpretU16AsF16(0x0400) }, 804 { target: -1.91e-6, expect: reinterpretU16AsF16(0x0400) }, // negative subnormal 805 { target: kValue.f16.negative.subnormal.max, expect: reinterpretU16AsF16(0x0400) }, 806 807 // Normals 808 { target: kValue.f16.positive.min, expect: reinterpretU16AsF16(0x0001) }, 809 { target: 1, expect: reinterpretU16AsF16(0x1000) }, 810 { target: 2, expect: reinterpretU16AsF16(0x1400) }, 811 { target: 4, expect: reinterpretU16AsF16(0x1800) }, 812 { target: 1000, expect: reinterpretU16AsF16(0x3800) }, 813 { target: kValue.f16.positive.max, expect: reinterpretU16AsF16(0x5000) }, 814 { target: kValue.f16.negative.max, expect: reinterpretU16AsF16(0x0001) }, 815 { target: -1, expect: reinterpretU16AsF16(0x1000) }, 816 { target: -2, expect: reinterpretU16AsF16(0x1400) }, 817 { target: -4, expect: reinterpretU16AsF16(0x1800) }, 818 { target: -1000, expect: reinterpretU16AsF16(0x3800) }, 819 { target: kValue.f16.negative.min, expect: reinterpretU16AsF16(0x5000) }, 820 821 // No precise f16 value 822 { target: 0.001, expect: reinterpretU16AsF16(0x0010) }, // positive normal 823 { target: -0.001, expect: reinterpretU16AsF16(0x0010) }, // negative normal 824 { target: 1e8, expect: reinterpretU16AsF16(0x5000) }, // positive out of range 825 { target: -1e8, expect: reinterpretU16AsF16(0x5000) }, // negative out of range 826 ]) 827 .fn(t => { 828 const target = t.params.target; 829 const got = oneULPF16(target, 'flush'); 830 const expect = t.params.expect; 831 t.expect( 832 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 833 `oneULPF16(${target}, 'flush') returned ${got}. Expected ${expect}` 834 ); 835 }); 836 837 g.test('oneULPF16NoFlush') 838 .paramsSubcasesOnly<OneULPCase>([ 839 // Edge Cases 840 { target: Number.NaN, expect: Number.NaN }, 841 { target: Number.POSITIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) }, 842 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) }, 843 844 // Zeroes, expect positive.min in flush mode 845 { target: +0, expect: reinterpretU16AsF16(0x0001) }, 846 { target: -0, expect: reinterpretU16AsF16(0x0001) }, 847 848 // Subnormals 849 { target: kValue.f16.positive.subnormal.min, expect: reinterpretU16AsF16(0x0001) }, 850 { target: 1.91e-6, expect: reinterpretU16AsF16(0x0001) }, // positive subnormal 851 { target: kValue.f16.positive.subnormal.max, expect: reinterpretU16AsF16(0x0001) }, 852 { target: kValue.f16.negative.subnormal.min, expect: reinterpretU16AsF16(0x0001) }, 853 { target: -1.91e-6, expect: reinterpretU16AsF16(0x0001) }, // negative subnormal 854 { target: kValue.f16.negative.subnormal.max, expect: reinterpretU16AsF16(0x0001) }, 855 856 // Normals 857 { target: kValue.f16.positive.min, expect: reinterpretU16AsF16(0x0001) }, 858 { target: 1, expect: reinterpretU16AsF16(0x1000) }, 859 { target: 2, expect: reinterpretU16AsF16(0x1400) }, 860 { target: 4, expect: reinterpretU16AsF16(0x1800) }, 861 { target: 1000, expect: reinterpretU16AsF16(0x3800) }, 862 { target: kValue.f16.positive.max, expect: reinterpretU16AsF16(0x5000) }, 863 { target: kValue.f16.negative.max, expect: reinterpretU16AsF16(0x0001) }, 864 { target: -1, expect: reinterpretU16AsF16(0x1000) }, 865 { target: -2, expect: reinterpretU16AsF16(0x1400) }, 866 { target: -4, expect: reinterpretU16AsF16(0x1800) }, 867 { target: -1000, expect: reinterpretU16AsF16(0x3800) }, 868 { target: kValue.f16.negative.min, expect: reinterpretU16AsF16(0x5000) }, 869 870 // No precise f16 value 871 { target: 0.001, expect: reinterpretU16AsF16(0x0010) }, // positive normal 872 { target: -0.001, expect: reinterpretU16AsF16(0x0010) }, // negative normal 873 { target: 1e8, expect: reinterpretU16AsF16(0x5000) }, // positive out of range 874 { target: -1e8, expect: reinterpretU16AsF16(0x5000) }, // negative out of range 875 ]) 876 .fn(t => { 877 const target = t.params.target; 878 const got = oneULPF16(target, 'no-flush'); 879 const expect = t.params.expect; 880 t.expect( 881 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 882 `oneULPF16(${target}, no-flush) returned ${got}. Expected ${expect}` 883 ); 884 }); 885 886 g.test('oneULPF16') 887 .paramsSubcasesOnly<OneULPCase>([ 888 // Edge Cases 889 { target: Number.NaN, expect: Number.NaN }, 890 { target: Number.POSITIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) }, 891 { target: Number.NEGATIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) }, 892 893 // Zeroes, expect positive.min in flush mode 894 { target: +0, expect: reinterpretU16AsF16(0x0400) }, 895 { target: -0, expect: reinterpretU16AsF16(0x0400) }, 896 897 // Subnormals 898 { target: kValue.f16.positive.subnormal.min, expect: reinterpretU16AsF16(0x0400) }, 899 { target: 1.91e-6, expect: reinterpretU16AsF16(0x0400) }, // positive subnormal 900 { target: kValue.f16.positive.subnormal.max, expect: reinterpretU16AsF16(0x0400) }, 901 { target: kValue.f16.negative.subnormal.min, expect: reinterpretU16AsF16(0x0400) }, 902 { target: -1.91e-6, expect: reinterpretU16AsF16(0x0400) }, // negative subnormal 903 { target: kValue.f16.negative.subnormal.max, expect: reinterpretU16AsF16(0x0400) }, 904 905 // Normals 906 { target: kValue.f16.positive.min, expect: reinterpretU16AsF16(0x0001) }, 907 { target: 1, expect: reinterpretU16AsF16(0x1000) }, 908 { target: 2, expect: reinterpretU16AsF16(0x1400) }, 909 { target: 4, expect: reinterpretU16AsF16(0x1800) }, 910 { target: 1000, expect: reinterpretU16AsF16(0x3800) }, 911 { target: kValue.f16.positive.max, expect: reinterpretU16AsF16(0x5000) }, 912 { target: kValue.f16.negative.max, expect: reinterpretU16AsF16(0x0001) }, 913 { target: -1, expect: reinterpretU16AsF16(0x1000) }, 914 { target: -2, expect: reinterpretU16AsF16(0x1400) }, 915 { target: -4, expect: reinterpretU16AsF16(0x1800) }, 916 { target: -1000, expect: reinterpretU16AsF16(0x3800) }, 917 { target: kValue.f16.negative.min, expect: reinterpretU16AsF16(0x5000) }, 918 919 // No precise f16 value 920 { target: 0.001, expect: reinterpretU16AsF16(0x0010) }, // positive normal 921 { target: -0.001, expect: reinterpretU16AsF16(0x0010) }, // negative normal 922 { target: 1e8, expect: reinterpretU16AsF16(0x5000) }, // positive out of range 923 { target: -1e8, expect: reinterpretU16AsF16(0x5000) }, // negative out of range 924 ]) 925 .fn(t => { 926 const target = t.params.target; 927 const got = oneULPF16(target, 'flush'); 928 const expect = t.params.expect; 929 t.expect( 930 got === expect || (Number.isNaN(got) && Number.isNaN(expect)), 931 `oneULPF16(${target}, 'flush') returned ${got}. Expected ${expect}` 932 ); 933 }); 934 935 interface correctlyRoundedCase { 936 value: number; 937 expected: Array<number>; 938 } 939 940 g.test('correctlyRoundedF32') 941 .paramsSubcasesOnly<correctlyRoundedCase>( 942 // prettier-ignore 943 [ 944 // Edge Cases 945 { value: kValue.f32.positive.max, expected: [kValue.f32.positive.max] }, 946 { value: kValue.f32.negative.min, expected: [kValue.f32.negative.min] }, 947 { value: kValue.f32.positive.max + oneULPF64(kValue.f32.positive.max), expected: [kValue.f32.positive.max, Number.POSITIVE_INFINITY] }, 948 { value: kValue.f32.negative.min - oneULPF64(kValue.f32.negative.min), expected: [Number.NEGATIVE_INFINITY, kValue.f32.negative.min] }, 949 { value: 2 ** (kValue.f32.emax + 1) - oneULPF64(kValue.f32.positive.max), expected: [kValue.f32.positive.max, Number.POSITIVE_INFINITY] }, 950 { value: -(2 ** (kValue.f32.emax + 1)) + oneULPF64(kValue.f32.positive.max), expected: [Number.NEGATIVE_INFINITY, kValue.f32.negative.min] }, 951 { value: 2 ** (kValue.f32.emax + 1), expected: [Number.POSITIVE_INFINITY] }, 952 { value: -(2 ** (kValue.f32.emax + 1)), expected: [Number.NEGATIVE_INFINITY] }, 953 { value: kValue.f32.positive.infinity, expected: [Number.POSITIVE_INFINITY] }, 954 { value: kValue.f32.negative.infinity, expected: [Number.NEGATIVE_INFINITY] }, 955 956 // 32-bit subnormals 957 { value: kValue.f32.positive.subnormal.min, expected: [kValue.f32.positive.subnormal.min] }, 958 { value: kValue.f32.positive.subnormal.max, expected: [kValue.f32.positive.subnormal.max] }, 959 { value: kValue.f32.negative.subnormal.min, expected: [kValue.f32.negative.subnormal.min] }, 960 { value: kValue.f32.negative.subnormal.max, expected: [kValue.f32.negative.subnormal.max] }, 961 962 // 64-bit subnormals 963 { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), expected: [0, kValue.f32.positive.subnormal.min] }, 964 { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), expected: [0, kValue.f32.positive.subnormal.min] }, 965 { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), expected: [kValue.f32.negative.subnormal.max, 0] }, 966 { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), expected: [kValue.f32.negative.subnormal.max, 0] }, 967 968 // 32-bit normals 969 { value: 0, expected: [0] }, 970 { value: kValue.f32.positive.min, expected: [kValue.f32.positive.min] }, 971 { value: kValue.f32.negative.max, expected: [kValue.f32.negative.max] }, 972 { value: reinterpretU32AsF32(0x03800000), expected: [reinterpretU32AsF32(0x03800000)] }, 973 { value: reinterpretU32AsF32(0x03800001), expected: [reinterpretU32AsF32(0x03800001)] }, 974 { value: reinterpretU32AsF32(0x83800000), expected: [reinterpretU32AsF32(0x83800000)] }, 975 { value: reinterpretU32AsF32(0x83800001), expected: [reinterpretU32AsF32(0x83800001)] }, 976 977 // 64-bit normals 978 { value: reinterpretU64AsF64(0x3ff0_0000_0000_0001n), expected: [reinterpretU32AsF32(0x3f800000), reinterpretU32AsF32(0x3f800001)] }, 979 { value: reinterpretU64AsF64(0x3ff0_0000_0000_0002n), expected: [reinterpretU32AsF32(0x3f800000), reinterpretU32AsF32(0x3f800001)] }, 980 { value: reinterpretU64AsF64(0x3ff0_0010_0000_0010n), expected: [reinterpretU32AsF32(0x3f800080), reinterpretU32AsF32(0x3f800081)] }, 981 { value: reinterpretU64AsF64(0x3ff0_0020_0000_0020n), expected: [reinterpretU32AsF32(0x3f800100), reinterpretU32AsF32(0x3f800101)] }, 982 { value: reinterpretU64AsF64(0xbff0_0000_0000_0001n), expected: [reinterpretU32AsF32(0xbf800001), reinterpretU32AsF32(0xbf800000)] }, 983 { value: reinterpretU64AsF64(0xbff0_0000_0000_0002n), expected: [reinterpretU32AsF32(0xbf800001), reinterpretU32AsF32(0xbf800000)] }, 984 { value: reinterpretU64AsF64(0xbff0_0010_0000_0010n), expected: [reinterpretU32AsF32(0xbf800081), reinterpretU32AsF32(0xbf800080)] }, 985 { value: reinterpretU64AsF64(0xbff0_0020_0000_0020n), expected: [reinterpretU32AsF32(0xbf800101), reinterpretU32AsF32(0xbf800100)] }, 986 ] 987 ) 988 .fn(t => { 989 const value = t.params.value; 990 const expected = t.params.expected; 991 992 const got = correctlyRoundedF32(value); 993 t.expect( 994 objectEquals(expected, got), 995 `correctlyRoundedF32(${f64(value)}) returned [${got.map(f32)}]. Expected [${expected.map( 996 f32 997 )}]` 998 ); 999 }); 1000 1001 g.test('correctlyRoundedF16') 1002 .paramsSubcasesOnly<correctlyRoundedCase>( 1003 // prettier-ignore 1004 [ 1005 // Edge Cases 1006 { value: kValue.f16.positive.max, expected: [kValue.f16.positive.max] }, 1007 { value: kValue.f16.negative.min, expected: [kValue.f16.negative.min] }, 1008 { value: kValue.f16.positive.max + oneULPF64(kValue.f16.positive.max), expected: [kValue.f16.positive.max, Number.POSITIVE_INFINITY] }, 1009 { value: kValue.f16.negative.min - oneULPF64(kValue.f16.negative.min), expected: [Number.NEGATIVE_INFINITY, kValue.f16.negative.min] }, 1010 { value: 2 ** (kValue.f16.emax + 1) - oneULPF64(kValue.f16.positive.max), expected: [kValue.f16.positive.max, Number.POSITIVE_INFINITY] }, 1011 { value: -(2 ** (kValue.f16.emax + 1)) + oneULPF64(kValue.f16.positive.max), expected: [Number.NEGATIVE_INFINITY, kValue.f16.negative.min] }, 1012 { value: 2 ** (kValue.f16.emax + 1), expected: [Number.POSITIVE_INFINITY] }, 1013 { value: -(2 ** (kValue.f16.emax + 1)), expected: [Number.NEGATIVE_INFINITY] }, 1014 { value: kValue.f16.positive.infinity, expected: [Number.POSITIVE_INFINITY] }, 1015 { value: kValue.f16.negative.infinity, expected: [Number.NEGATIVE_INFINITY] }, 1016 1017 // 16-bit subnormals 1018 { value: kValue.f16.positive.subnormal.min, expected: [kValue.f16.positive.subnormal.min] }, 1019 { value: kValue.f16.positive.subnormal.max, expected: [kValue.f16.positive.subnormal.max] }, 1020 { value: kValue.f16.negative.subnormal.min, expected: [kValue.f16.negative.subnormal.min] }, 1021 { value: kValue.f16.negative.subnormal.max, expected: [kValue.f16.negative.subnormal.max] }, 1022 1023 // 32-bit subnormals 1024 { value: kValue.f32.positive.subnormal.min, expected: [0, kValue.f16.positive.subnormal.min] }, 1025 { value: kValue.f32.positive.subnormal.max, expected: [0, kValue.f16.positive.subnormal.min] }, 1026 { value: kValue.f32.negative.subnormal.max, expected: [kValue.f16.negative.subnormal.max, 0] }, 1027 { value: kValue.f32.negative.subnormal.min, expected: [kValue.f16.negative.subnormal.max, 0] }, 1028 1029 // 16-bit normals 1030 { value: 0, expected: [0] }, 1031 { value: kValue.f16.positive.min, expected: [kValue.f16.positive.min] }, 1032 { value: kValue.f16.negative.max, expected: [kValue.f16.negative.max] }, 1033 { value: reinterpretU16AsF16(0x1380), expected: [reinterpretU16AsF16(0x1380)] }, 1034 { value: reinterpretU16AsF16(0x1381), expected: [reinterpretU16AsF16(0x1381)] }, 1035 { value: reinterpretU16AsF16(0x9380), expected: [reinterpretU16AsF16(0x9380)] }, 1036 { value: reinterpretU16AsF16(0x9381), expected: [reinterpretU16AsF16(0x9381)] }, 1037 1038 // 32-bit normals 1039 { value: reinterpretU32AsF32(0x3a700001), expected: [reinterpretU16AsF16(0x1380), reinterpretU16AsF16(0x1381)] }, 1040 { value: reinterpretU32AsF32(0x3a700002), expected: [reinterpretU16AsF16(0x1380), reinterpretU16AsF16(0x1381)] }, 1041 { value: reinterpretU32AsF32(0xba700001), expected: [reinterpretU16AsF16(0x9381), reinterpretU16AsF16(0x9380)] }, 1042 { value: reinterpretU32AsF32(0xba700002), expected: [reinterpretU16AsF16(0x9381), reinterpretU16AsF16(0x9380)] }, 1043 ] 1044 ) 1045 .fn(t => { 1046 const value = t.params.value; 1047 const expected = t.params.expected; 1048 1049 const got = correctlyRoundedF16(value); 1050 t.expect( 1051 objectEquals(expected, got), 1052 `correctlyRoundedF16(${f64(value)}) returned [${got.map(f16)}]. Expected [${expected.map( 1053 f16 1054 )}]` 1055 ); 1056 }); 1057 1058 interface frexpCase { 1059 input: number; 1060 fract: number; 1061 exp: number; 1062 } 1063 1064 // prettier-ignore 1065 const kFrexpCases = { 1066 f32: [ 1067 { input: kValue.f32.positive.max, fract: 0.9999999403953552, exp: 128 }, 1068 { input: kValue.f32.positive.min, fract: 0.5, exp: -125 }, 1069 { input: kValue.f32.negative.max, fract: -0.5, exp: -125 }, 1070 { input: kValue.f32.negative.min, fract: -0.9999999403953552, exp: 128 }, 1071 { input: kValue.f32.positive.subnormal.max, fract: 0.9999998807907104, exp: -126 }, 1072 { input: kValue.f32.positive.subnormal.min, fract: 0.5, exp: -148 }, 1073 { input: kValue.f32.negative.subnormal.max, fract: -0.5, exp: -148 }, 1074 { input: kValue.f32.negative.subnormal.min, fract: -0.9999998807907104, exp: -126 }, 1075 ] as frexpCase[], 1076 f16: [ 1077 { input: kValue.f16.positive.max, fract: 0.99951171875, exp: 16 }, 1078 { input: kValue.f16.positive.min, fract: 0.5, exp: -13 }, 1079 { input: kValue.f16.negative.max, fract: -0.5, exp: -13 }, 1080 { input: kValue.f16.negative.min, fract: -0.99951171875, exp: 16 }, 1081 { input: kValue.f16.positive.subnormal.max, fract: 0.9990234375, exp: -14 }, 1082 { input: kValue.f16.positive.subnormal.min, fract: 0.5, exp: -23 }, 1083 { input: kValue.f16.negative.subnormal.max, fract: -0.5, exp: -23 }, 1084 { input: kValue.f16.negative.subnormal.min, fract: -0.9990234375, exp: -14 }, 1085 ] as frexpCase[], 1086 f64: [ 1087 { input: kValue.f64.positive.max, fract: reinterpretU64AsF64(0x3fef_ffff_ffff_ffffn) /* ~0.9999999999999999 */, exp: 1024 }, 1088 { input: kValue.f64.positive.min, fract: 0.5, exp: -1021 }, 1089 { input: kValue.f64.negative.max, fract: -0.5, exp: -1021 }, 1090 { input: kValue.f64.negative.min, fract: reinterpretU64AsF64(0xbfef_ffff_ffff_ffffn) /* ~-0.9999999999999999 */, exp: 1024 }, 1091 { input: kValue.f64.positive.subnormal.max, fract: reinterpretU64AsF64(0x3fef_ffff_ffff_fffen) /* ~0.9999999999999998 */, exp: -1022 }, 1092 { input: kValue.f64.positive.subnormal.min, fract: 0.5, exp: -1073 }, 1093 { input: kValue.f64.negative.subnormal.max, fract: -0.5, exp: -1073 }, 1094 { input: kValue.f64.negative.subnormal.min, fract: reinterpretU64AsF64(0xbfef_ffff_ffff_fffen) /* ~-0.9999999999999998 */, exp: -1022 }, 1095 ] as frexpCase[], 1096 } as const; 1097 1098 g.test('frexp') 1099 .params(u => 1100 u 1101 .combine('trait', ['f32', 'f16', 'f64'] as const) 1102 .beginSubcases() 1103 .expandWithParams<frexpCase>(p => { 1104 // prettier-ignore 1105 return [ 1106 // +/- 0.0 1107 { input: 0, fract: 0, exp: 0 }, 1108 { input: -0, fract: -0, exp: 0 }, 1109 // Normal float values that can be exactly represented by all float types 1110 { input: 0.171875, fract: 0.6875, exp: -2 }, 1111 { input: -0.171875, fract: -0.6875, exp: -2 }, 1112 { input: 0.5, fract: 0.5, exp: 0 }, 1113 { input: -0.5, fract: -0.5, exp: 0 }, 1114 { input: 1, fract: 0.5, exp: 1 }, 1115 { input: -1, fract: -0.5, exp: 1 }, 1116 { input: 2, fract: 0.5, exp: 2 }, 1117 { input: -2, fract: -0.5, exp: 2 }, 1118 { input: 10000, fract: 0.6103515625, exp: 14 }, 1119 { input: -10000, fract: -0.6103515625, exp: 14 }, 1120 // Normal ans subnormal cases that are different for each type 1121 ...kFrexpCases[p.trait], 1122 // Inf and NaN 1123 { input: Number.POSITIVE_INFINITY, fract: Number.POSITIVE_INFINITY, exp: 0 }, 1124 { input: Number.NEGATIVE_INFINITY, fract: Number.NEGATIVE_INFINITY, exp: 0 }, 1125 { input: Number.NaN, fract: Number.NaN, exp: 0 }, 1126 ]; 1127 }) 1128 ) 1129 .fn(test => { 1130 const input = test.params.input; 1131 const got = frexp(input, test.params.trait); 1132 const expect = { fract: test.params.fract, exp: test.params.exp }; 1133 1134 test.expect( 1135 objectEquals(got, expect), 1136 `frexp(${input}, ${test.params.trait}) returned { fract: ${got.fract}, exp: ${got.exp} }. Expected { fract: ${expect.fract}, exp: ${expect.exp} }` 1137 ); 1138 }); 1139 1140 interface lerpCase { 1141 a: number; 1142 b: number; 1143 t: number; 1144 result: number; 1145 } 1146 1147 g.test('lerp') 1148 .paramsSimple<lerpCase>([ 1149 // Infinite cases 1150 { a: 0.0, b: Number.POSITIVE_INFINITY, t: 0.5, result: Number.NaN }, 1151 { a: Number.POSITIVE_INFINITY, b: 0.0, t: 0.5, result: Number.NaN }, 1152 { a: Number.NEGATIVE_INFINITY, b: 1.0, t: 0.5, result: Number.NaN }, 1153 { a: 1.0, b: Number.NEGATIVE_INFINITY, t: 0.5, result: Number.NaN }, 1154 { a: Number.NEGATIVE_INFINITY, b: Number.POSITIVE_INFINITY, t: 0.5, result: Number.NaN }, 1155 { a: Number.POSITIVE_INFINITY, b: Number.NEGATIVE_INFINITY, t: 0.5, result: Number.NaN }, 1156 { a: 0.0, b: 1.0, t: Number.NEGATIVE_INFINITY, result: Number.NaN }, 1157 { a: 1.0, b: 0.0, t: Number.NEGATIVE_INFINITY, result: Number.NaN }, 1158 { a: 0.0, b: 1.0, t: Number.POSITIVE_INFINITY, result: Number.NaN }, 1159 { a: 1.0, b: 0.0, t: Number.POSITIVE_INFINITY, result: Number.NaN }, 1160 1161 // [0.0, 1.0] cases 1162 { a: 0.0, b: 1.0, t: -1.0, result: -1.0 }, 1163 { a: 0.0, b: 1.0, t: 0.0, result: 0.0 }, 1164 { a: 0.0, b: 1.0, t: 0.1, result: 0.1 }, 1165 { a: 0.0, b: 1.0, t: 0.01, result: 0.01 }, 1166 { a: 0.0, b: 1.0, t: 0.001, result: 0.001 }, 1167 { a: 0.0, b: 1.0, t: 0.25, result: 0.25 }, 1168 { a: 0.0, b: 1.0, t: 0.5, result: 0.5 }, 1169 { a: 0.0, b: 1.0, t: 0.9, result: 0.9 }, 1170 { a: 0.0, b: 1.0, t: 0.99, result: 0.99 }, 1171 { a: 0.0, b: 1.0, t: 0.999, result: 0.999 }, 1172 { a: 0.0, b: 1.0, t: 1.0, result: 1.0 }, 1173 { a: 0.0, b: 1.0, t: 2.0, result: 2.0 }, 1174 1175 // [1.0, 0.0] cases 1176 { a: 1.0, b: 0.0, t: -1.0, result: 2.0 }, 1177 { a: 1.0, b: 0.0, t: 0.0, result: 1.0 }, 1178 { a: 1.0, b: 0.0, t: 0.1, result: 0.9 }, 1179 { a: 1.0, b: 0.0, t: 0.01, result: 0.99 }, 1180 { a: 1.0, b: 0.0, t: 0.001, result: 0.999 }, 1181 { a: 1.0, b: 0.0, t: 0.25, result: 0.75 }, 1182 { a: 1.0, b: 0.0, t: 0.5, result: 0.5 }, 1183 { a: 1.0, b: 0.0, t: 0.9, result: 0.1 }, 1184 { a: 1.0, b: 0.0, t: 0.99, result: 0.01 }, 1185 { a: 1.0, b: 0.0, t: 0.999, result: 0.001 }, 1186 { a: 1.0, b: 0.0, t: 1.0, result: 0.0 }, 1187 { a: 1.0, b: 0.0, t: 2.0, result: -1.0 }, 1188 1189 // [0.0, 10.0] cases 1190 { a: 0.0, b: 10.0, t: -1.0, result: -10.0 }, 1191 { a: 0.0, b: 10.0, t: 0.0, result: 0.0 }, 1192 { a: 0.0, b: 10.0, t: 0.1, result: 1.0 }, 1193 { a: 0.0, b: 10.0, t: 0.01, result: 0.1 }, 1194 { a: 0.0, b: 10.0, t: 0.001, result: 0.01 }, 1195 { a: 0.0, b: 10.0, t: 0.25, result: 2.5 }, 1196 { a: 0.0, b: 10.0, t: 0.5, result: 5.0 }, 1197 { a: 0.0, b: 10.0, t: 0.9, result: 9.0 }, 1198 { a: 0.0, b: 10.0, t: 0.99, result: 9.9 }, 1199 { a: 0.0, b: 10.0, t: 0.999, result: 9.99 }, 1200 { a: 0.0, b: 10.0, t: 1.0, result: 10.0 }, 1201 { a: 0.0, b: 10.0, t: 2.0, result: 20.0 }, 1202 1203 // [10.0, 0.0] cases 1204 { a: 10.0, b: 0.0, t: -1.0, result: 20.0 }, 1205 { a: 10.0, b: 0.0, t: 0.0, result: 10.0 }, 1206 { a: 10.0, b: 0.0, t: 0.1, result: 9 }, 1207 { a: 10.0, b: 0.0, t: 0.01, result: 9.9 }, 1208 { a: 10.0, b: 0.0, t: 0.001, result: 9.99 }, 1209 { a: 10.0, b: 0.0, t: 0.25, result: 7.5 }, 1210 { a: 10.0, b: 0.0, t: 0.5, result: 5.0 }, 1211 { a: 10.0, b: 0.0, t: 0.9, result: 1.0 }, 1212 { a: 10.0, b: 0.0, t: 0.99, result: 0.1 }, 1213 { a: 10.0, b: 0.0, t: 0.999, result: 0.01 }, 1214 { a: 10.0, b: 0.0, t: 1.0, result: 0.0 }, 1215 { a: 10.0, b: 0.0, t: 2.0, result: -10.0 }, 1216 1217 // [2.0, 10.0] cases 1218 { a: 2.0, b: 10.0, t: -1.0, result: -6.0 }, 1219 { a: 2.0, b: 10.0, t: 0.0, result: 2.0 }, 1220 { a: 2.0, b: 10.0, t: 0.1, result: 2.8 }, 1221 { a: 2.0, b: 10.0, t: 0.01, result: 2.08 }, 1222 { a: 2.0, b: 10.0, t: 0.001, result: 2.008 }, 1223 { a: 2.0, b: 10.0, t: 0.25, result: 4.0 }, 1224 { a: 2.0, b: 10.0, t: 0.5, result: 6.0 }, 1225 { a: 2.0, b: 10.0, t: 0.9, result: 9.2 }, 1226 { a: 2.0, b: 10.0, t: 0.99, result: 9.92 }, 1227 { a: 2.0, b: 10.0, t: 0.999, result: 9.992 }, 1228 { a: 2.0, b: 10.0, t: 1.0, result: 10.0 }, 1229 { a: 2.0, b: 10.0, t: 2.0, result: 18.0 }, 1230 1231 // [10.0, 2.0] cases 1232 { a: 10.0, b: 2.0, t: -1.0, result: 18.0 }, 1233 { a: 10.0, b: 2.0, t: 0.0, result: 10.0 }, 1234 { a: 10.0, b: 2.0, t: 0.1, result: 9.2 }, 1235 { a: 10.0, b: 2.0, t: 0.01, result: 9.92 }, 1236 { a: 10.0, b: 2.0, t: 0.001, result: 9.992 }, 1237 { a: 10.0, b: 2.0, t: 0.25, result: 8.0 }, 1238 { a: 10.0, b: 2.0, t: 0.5, result: 6.0 }, 1239 { a: 10.0, b: 2.0, t: 0.9, result: 2.8 }, 1240 { a: 10.0, b: 2.0, t: 0.99, result: 2.08 }, 1241 { a: 10.0, b: 2.0, t: 0.999, result: 2.008 }, 1242 { a: 10.0, b: 2.0, t: 1.0, result: 2.0 }, 1243 { a: 10.0, b: 2.0, t: 2.0, result: -6.0 }, 1244 1245 // [-1.0, 1.0] cases 1246 { a: -1.0, b: 1.0, t: -2.0, result: -5.0 }, 1247 { a: -1.0, b: 1.0, t: 0.0, result: -1.0 }, 1248 { a: -1.0, b: 1.0, t: 0.1, result: -0.8 }, 1249 { a: -1.0, b: 1.0, t: 0.01, result: -0.98 }, 1250 { a: -1.0, b: 1.0, t: 0.001, result: -0.998 }, 1251 { a: -1.0, b: 1.0, t: 0.25, result: -0.5 }, 1252 { a: -1.0, b: 1.0, t: 0.5, result: 0.0 }, 1253 { a: -1.0, b: 1.0, t: 0.9, result: 0.8 }, 1254 { a: -1.0, b: 1.0, t: 0.99, result: 0.98 }, 1255 { a: -1.0, b: 1.0, t: 0.999, result: 0.998 }, 1256 { a: -1.0, b: 1.0, t: 1.0, result: 1.0 }, 1257 { a: -1.0, b: 1.0, t: 2.0, result: 3.0 }, 1258 1259 // [1.0, -1.0] cases 1260 { a: 1.0, b: -1.0, t: -2.0, result: 5.0 }, 1261 { a: 1.0, b: -1.0, t: 0.0, result: 1.0 }, 1262 { a: 1.0, b: -1.0, t: 0.1, result: 0.8 }, 1263 { a: 1.0, b: -1.0, t: 0.01, result: 0.98 }, 1264 { a: 1.0, b: -1.0, t: 0.001, result: 0.998 }, 1265 { a: 1.0, b: -1.0, t: 0.25, result: 0.5 }, 1266 { a: 1.0, b: -1.0, t: 0.5, result: 0.0 }, 1267 { a: 1.0, b: -1.0, t: 0.9, result: -0.8 }, 1268 { a: 1.0, b: -1.0, t: 0.99, result: -0.98 }, 1269 { a: 1.0, b: -1.0, t: 0.999, result: -0.998 }, 1270 { a: 1.0, b: -1.0, t: 1.0, result: -1.0 }, 1271 { a: 1.0, b: -1.0, t: 2.0, result: -3.0 }, 1272 1273 // [-1.0, 0.0] cases 1274 { a: -1.0, b: 0.0, t: -1.0, result: -2.0 }, 1275 { a: -1.0, b: 0.0, t: 0.0, result: -1.0 }, 1276 { a: -1.0, b: 0.0, t: 0.1, result: -0.9 }, 1277 { a: -1.0, b: 0.0, t: 0.01, result: -0.99 }, 1278 { a: -1.0, b: 0.0, t: 0.001, result: -0.999 }, 1279 { a: -1.0, b: 0.0, t: 0.25, result: -0.75 }, 1280 { a: -1.0, b: 0.0, t: 0.5, result: -0.5 }, 1281 { a: -1.0, b: 0.0, t: 0.9, result: -0.1 }, 1282 { a: -1.0, b: 0.0, t: 0.99, result: -0.01 }, 1283 { a: -1.0, b: 0.0, t: 0.999, result: -0.001 }, 1284 { a: -1.0, b: 0.0, t: 1.0, result: 0.0 }, 1285 { a: -1.0, b: 0.0, t: 2.0, result: 1.0 }, 1286 1287 // [0.0, -1.0] cases 1288 { a: 0.0, b: -1.0, t: -1.0, result: 1.0 }, 1289 { a: 0.0, b: -1.0, t: 0.0, result: 0.0 }, 1290 { a: 0.0, b: -1.0, t: 0.1, result: -0.1 }, 1291 { a: 0.0, b: -1.0, t: 0.01, result: -0.01 }, 1292 { a: 0.0, b: -1.0, t: 0.001, result: -0.001 }, 1293 { a: 0.0, b: -1.0, t: 0.25, result: -0.25 }, 1294 { a: 0.0, b: -1.0, t: 0.5, result: -0.5 }, 1295 { a: 0.0, b: -1.0, t: 0.9, result: -0.9 }, 1296 { a: 0.0, b: -1.0, t: 0.99, result: -0.99 }, 1297 { a: 0.0, b: -1.0, t: 0.999, result: -0.999 }, 1298 { a: 0.0, b: -1.0, t: 1.0, result: -1.0 }, 1299 { a: 0.0, b: -1.0, t: 2.0, result: -2.0 }, 1300 ]) 1301 .fn(test => { 1302 const a = test.params.a; 1303 const b = test.params.b; 1304 const t = test.params.t; 1305 const got = lerp(a, b, t); 1306 const expect = test.params.result; 1307 1308 test.expect( 1309 (Number.isNaN(got) && Number.isNaN(expect)) || withinOneULPF32(got, expect, 'flush'), 1310 `lerp(${a}, ${b}, ${t}) returned ${got}. Expected ${expect}` 1311 ); 1312 }); 1313 1314 interface lerpBigIntCase { 1315 a: bigint; 1316 b: bigint; 1317 idx: number; 1318 steps: number; 1319 result: bigint; 1320 } 1321 1322 g.test('lerpBigInt') 1323 .paramsSimple<lerpBigIntCase>([ 1324 // [0n, 1000n] cases 1325 { a: 0n, b: 1000n, idx: 0, steps: 1, result: 0n }, 1326 { a: 0n, b: 1000n, idx: 0, steps: 2, result: 0n }, 1327 { a: 0n, b: 1000n, idx: 1, steps: 2, result: 1000n }, 1328 { a: 0n, b: 1000n, idx: 0, steps: 1000, result: 0n }, 1329 { a: 0n, b: 1000n, idx: 500, steps: 1000, result: 500n }, 1330 { a: 0n, b: 1000n, idx: 999, steps: 1000, result: 1000n }, 1331 1332 // [1000n, 0n] cases 1333 { a: 1000n, b: 0n, idx: 0, steps: 1, result: 1000n }, 1334 { a: 1000n, b: 0n, idx: 0, steps: 2, result: 1000n }, 1335 { a: 1000n, b: 0n, idx: 1, steps: 2, result: 0n }, 1336 { a: 1000n, b: 0n, idx: 0, steps: 1000, result: 1000n }, 1337 { a: 1000n, b: 0n, idx: 500, steps: 1000, result: 500n }, 1338 { a: 1000n, b: 0n, idx: 999, steps: 1000, result: 0n }, 1339 1340 // [0n, -1000n] cases 1341 { a: 0n, b: -1000n, idx: 0, steps: 1, result: 0n }, 1342 { a: 0n, b: -1000n, idx: 0, steps: 2, result: 0n }, 1343 { a: 0n, b: -1000n, idx: 1, steps: 2, result: -1000n }, 1344 { a: 0n, b: -1000n, idx: 0, steps: 1000, result: 0n }, 1345 { a: 0n, b: -1000n, idx: 500, steps: 1000, result: -500n }, 1346 { a: 0n, b: -1000n, idx: 999, steps: 1000, result: -1000n }, 1347 1348 // [-1000n, 0n] cases 1349 { a: -1000n, b: 0n, idx: 0, steps: 1, result: -1000n }, 1350 { a: -1000n, b: 0n, idx: 0, steps: 2, result: -1000n }, 1351 { a: -1000n, b: 0n, idx: 1, steps: 2, result: 0n }, 1352 { a: -1000n, b: 0n, idx: 0, steps: 1000, result: -1000n }, 1353 { a: -1000n, b: 0n, idx: 500, steps: 1000, result: -500n }, 1354 { a: -1000n, b: 0n, idx: 999, steps: 1000, result: 0n }, 1355 1356 // [100n, 1000n] cases 1357 { a: 100n, b: 1000n, idx: 0, steps: 1, result: 100n }, 1358 { a: 100n, b: 1000n, idx: 0, steps: 2, result: 100n }, 1359 { a: 100n, b: 1000n, idx: 1, steps: 2, result: 1000n }, 1360 { a: 100n, b: 1000n, idx: 0, steps: 9, result: 100n }, 1361 { a: 100n, b: 1000n, idx: 4, steps: 9, result: 550n }, 1362 { a: 100n, b: 1000n, idx: 8, steps: 9, result: 1000n }, 1363 1364 // [1000n, 100n] cases 1365 { a: 1000n, b: 100n, idx: 0, steps: 1, result: 1000n }, 1366 { a: 1000n, b: 100n, idx: 0, steps: 2, result: 1000n }, 1367 { a: 1000n, b: 100n, idx: 1, steps: 2, result: 100n }, 1368 { a: 1000n, b: 100n, idx: 0, steps: 9, result: 1000n }, 1369 { a: 1000n, b: 100n, idx: 4, steps: 9, result: 550n }, 1370 { a: 1000n, b: 100n, idx: 8, steps: 9, result: 100n }, 1371 1372 // [01000n, 1000n] cases 1373 { a: -1000n, b: 1000n, idx: 0, steps: 1, result: -1000n }, 1374 { a: -1000n, b: 1000n, idx: 0, steps: 2, result: -1000n }, 1375 { a: -1000n, b: 1000n, idx: 1, steps: 2, result: 1000n }, 1376 { a: -1000n, b: 1000n, idx: 0, steps: 9, result: -1000n }, 1377 { a: -1000n, b: 1000n, idx: 4, steps: 9, result: 0n }, 1378 { a: -1000n, b: 1000n, idx: 8, steps: 9, result: 1000n }, 1379 ]) 1380 .fn(test => { 1381 const a = test.params.a; 1382 const b = test.params.b; 1383 const idx = test.params.idx; 1384 const steps = test.params.steps; 1385 const got = lerpBigInt(a, b, idx, steps); 1386 const expect = test.params.result; 1387 1388 test.expect( 1389 got === expect, 1390 `lerpBigInt(${a}, ${b}, ${idx}, ${steps}) returned ${got}. Expected ${expect}` 1391 ); 1392 }); 1393 1394 interface rangeCase { 1395 a: number; 1396 b: number; 1397 num_steps: number; 1398 result: number[]; 1399 } 1400 1401 g.test('linearRange') 1402 .paramsSimple<rangeCase>( 1403 // prettier-ignore 1404 [ 1405 { a: 0.0, b: Number.POSITIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1406 { a: Number.POSITIVE_INFINITY, b: 0.0, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1407 { a: Number.NEGATIVE_INFINITY, b: 1.0, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1408 { a: 1.0, b: Number.NEGATIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1409 { a: Number.NEGATIVE_INFINITY, b: Number.POSITIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1410 { a: Number.POSITIVE_INFINITY, b: Number.NEGATIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1411 { a: 0.0, b: 0.0, num_steps: 10, result: new Array<number>(10).fill(0.0) }, 1412 { a: 10.0, b: 10.0, num_steps: 10, result: new Array<number>(10).fill(10.0) }, 1413 { a: 0.0, b: 10.0, num_steps: 1, result: [0.0] }, 1414 { a: 10.0, b: 0.0, num_steps: 1, result: [10] }, 1415 { a: 0.0, b: 10.0, num_steps: 11, result: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] }, 1416 { a: 10.0, b: 0.0, num_steps: 11, result: [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0] }, 1417 { a: 0.0, b: 1000.0, num_steps: 11, result: [0.0, 100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0] }, 1418 { a: 1000.0, b: 0.0, num_steps: 11, result: [1000.0, 900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 100.0, 0.0] }, 1419 { a: 1.0, b: 5.0, num_steps: 5, result: [1.0, 2.0, 3.0, 4.0, 5.0] }, 1420 { a: 5.0, b: 1.0, num_steps: 5, result: [5.0, 4.0, 3.0, 2.0, 1.0] }, 1421 { a: 0.0, b: 1.0, num_steps: 11, result: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] }, 1422 { a: 1.0, b: 0.0, num_steps: 11, result: [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0] }, 1423 { a: 0.0, b: 1.0, num_steps: 5, result: [0.0, 0.25, 0.5, 0.75, 1.0] }, 1424 { a: 1.0, b: 0.0, num_steps: 5, result: [1.0, 0.75, 0.5, 0.25, 0.0] }, 1425 { a: -1.0, b: 1.0, num_steps: 11, result: [-1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0] }, 1426 { a: 1.0, b: -1.0, num_steps: 11, result: [1.0, 0.8, 0.6, 0.4, 0.2, 0.0, -0.2, -0.4, -0.6, -0.8, -1.0] }, 1427 { a: -1.0, b: 0, num_steps: 11, result: [-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0] }, 1428 { a: 0.0, b: -1.0, num_steps: 11, result: [0.0, -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1.0] }, 1429 ] 1430 ) 1431 .fn(test => { 1432 const a = test.params.a; 1433 const b = test.params.b; 1434 const num_steps = test.params.num_steps; 1435 const got = linearRange(a, b, num_steps); 1436 const expect = test.params.result; 1437 1438 test.expect( 1439 compareArrayOfNumbersF32(got, expect, 'no-flush'), 1440 `linearRange(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}` 1441 ); 1442 }); 1443 1444 g.test('biasedRange') 1445 .paramsSimple<rangeCase>( 1446 // prettier-ignore 1447 [ 1448 { a: 0.0, b: Number.POSITIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1449 { a: Number.POSITIVE_INFINITY, b: 0.0, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1450 { a: Number.NEGATIVE_INFINITY, b: 1.0, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1451 { a: 1.0, b: Number.NEGATIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1452 { a: Number.NEGATIVE_INFINITY, b: Number.POSITIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1453 { a: Number.POSITIVE_INFINITY, b: Number.NEGATIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) }, 1454 { a: 0.0, b: 0.0, num_steps: 10, result: new Array<number>(10).fill(0.0) }, 1455 { a: 10.0, b: 10.0, num_steps: 10, result: new Array<number>(10).fill(10.0) }, 1456 { a: 0.0, b: 10.0, num_steps: 1, result: [0.0] }, 1457 { a: 10.0, b: 0.0, num_steps: 1, result: [10.0] }, 1458 { a: 0.0, b: 10.0, num_steps: 11, result: [0.0, 0.1, 0.4, 0.9, 1.6, 2.5, 3.6, 4.9, 6.4, 8.1, 10.0] }, 1459 { a: 10.0, b: 0.0, num_steps: 11, result: [10.0, 9.9, 9.6, 9.1, 8.4, 7.5, 6.4, 5.1, 3.6, 1.9, 0.0] }, 1460 { a: 0.0, b: 1000.0, num_steps: 11, result: [0.0, 10.0, 40.0, 90.0, 160.0, 250.0, 360.0, 490.0, 640.0, 810.0, 1000.0] }, 1461 { a: 1000.0, b: 0.0, num_steps: 11, result: [1000.0, 990.0, 960.0, 910.0, 840.0, 750.0, 640.0, 510.0, 360.0, 190.0, 0.0] }, 1462 { a: 1.0, b: 5.0, num_steps: 5, result: [1.0, 1.25, 2.0, 3.25, 5.0] }, 1463 { a: 5.0, b: 1.0, num_steps: 5, result: [5.0, 4.75, 4.0, 2.75, 1.0] }, 1464 { a: 0.0, b: 1.0, num_steps: 11, result: [0.0, 0.01, 0.04, 0.09, 0.16, 0.25, 0.36, 0.49, 0.64, 0.81, 1.0] }, 1465 { a: 1.0, b: 0.0, num_steps: 11, result: [1.0, 0.99, 0.96, 0.91, 0.84, 0.75, 0.64, 0.51, 0.36, 0.19, 0.0] }, 1466 { a: 0.0, b: 1.0, num_steps: 5, result: [0.0, 0.0625, 0.25, 0.5625, 1.0] }, 1467 { a: 1.0, b: 0.0, num_steps: 5, result: [1.0, 0.9375, 0.75, 0.4375, 0.0] }, 1468 { a: -1.0, b: 1.0, num_steps: 11, result: [-1.0, -0.98, -0.92, -0.82, -0.68, -0.5, -0.28 ,-0.02, 0.28, 0.62, 1.0] }, 1469 { a: 1.0, b: -1.0, num_steps: 11, result: [1.0, 0.98, 0.92, 0.82, 0.68, 0.5, 0.28 ,0.02, -0.28, -0.62, -1.0] }, 1470 { a: -1.0, b: 0, num_steps: 11, result: [-1.0 , -0.99, -0.96, -0.91, -0.84, -0.75, -0.64, -0.51, -0.36, -0.19, 0.0] }, 1471 { a: 0.0, b: -1.0, num_steps: 11, result: [0.0, -0.01, -0.04, -0.09, -0.16, -0.25, -0.36, -0.49, -0.64, -0.81, -1.0] }, 1472 ] 1473 ) 1474 .fn(test => { 1475 const a = test.params.a; 1476 const b = test.params.b; 1477 const num_steps = test.params.num_steps; 1478 const got = biasedRange(a, b, num_steps); 1479 const expect = test.params.result; 1480 1481 test.expect( 1482 compareArrayOfNumbersF32(got, expect, 'no-flush'), 1483 `biasedRange(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}` 1484 ); 1485 }); 1486 1487 interface rangeBigIntCase { 1488 a: bigint; 1489 b: bigint; 1490 num_steps: number; 1491 result: bigint[]; 1492 } 1493 1494 g.test('linearRangeBigInt') 1495 .paramsSimple<rangeBigIntCase>( 1496 // prettier-ignore 1497 [ 1498 { a: 0n, b: 0n, num_steps: 10, result: new Array<bigint>(10).fill(0n) }, 1499 { a: 10n, b: 10n, num_steps: 10, result: new Array<bigint>(10).fill(10n) }, 1500 { a: 0n, b: 10n, num_steps: 1, result: [0n] }, 1501 { a: 10n, b: 0n, num_steps: 1, result: [10n] }, 1502 { a: 0n, b: 10n, num_steps: 11, result: [0n, 1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 10n] }, 1503 { a: 10n, b: 0n, num_steps: 11, result: [10n, 9n, 8n, 7n, 6n, 5n, 4n, 3n, 2n, 1n, 0n] }, 1504 { a: 0n, b: 1000n, num_steps: 11, result: [0n, 100n, 200n, 300n, 400n, 500n, 600n, 700n, 800n, 900n, 1000n] }, 1505 { a: 1000n, b: 0n, num_steps: 11, result: [1000n, 900n, 800n, 700n, 600n, 500n, 400n, 300n, 200n, 100n, 0n] }, 1506 { a: 1n, b: 5n, num_steps: 5, result: [1n, 2n, 3n, 4n, 5n] }, 1507 { a: 5n, b: 1n, num_steps: 5, result: [5n, 4n, 3n, 2n, 1n] }, 1508 { a: 0n, b: 10n, num_steps: 5, result: [0n, 2n, 5n, 7n, 10n] }, 1509 { a: 10n, b: 0n, num_steps: 5, result: [10n, 8n, 5n, 3n, 0n] }, 1510 { a: -10n, b: 10n, num_steps: 11, result: [-10n, -8n, -6n, -4n, -2n, 0n, 2n, 4n, 6n, 8n, 10n] }, 1511 { a: 10n, b: -10n, num_steps: 11, result: [10n, 8n, 6n, 4n, 2n, 0n, -2n, -4n, -6n, -8n, -10n] }, 1512 { a: -10n, b: 0n, num_steps: 11, result: [-10n, -9n, -8n, -7n, -6n, -5n, -4n, -3n, -2n, -1n, 0n] }, 1513 { a: 0n, b: -10n, num_steps: 11, result: [0n, -1n, -2n, -3n, -4n, -5n, -6n, -7n, -8n, -9n, -10n] }, 1514 ] 1515 ) 1516 .fn(test => { 1517 const a = test.params.a; 1518 const b = test.params.b; 1519 const num_steps = test.params.num_steps; 1520 const got = linearRangeBigInt(a, b, num_steps); 1521 const expect = test.params.result; 1522 1523 test.expect( 1524 objectEquals(got, expect), 1525 `linearRangeBigInt(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}` 1526 ); 1527 }); 1528 1529 g.test('biasedRangeBigInt') 1530 .paramsSimple<rangeBigIntCase>( 1531 // prettier-ignore 1532 [ 1533 { a: 0n, b: 0n, num_steps: 10, result: new Array<bigint>(10).fill(0n) }, 1534 { a: 10n, b: 10n, num_steps: 10, result: new Array<bigint>(10).fill(10n) }, 1535 { a: 0n, b: 10n, num_steps: 1, result: [0n] }, 1536 { a: 10n, b: 0n, num_steps: 1, result: [10n] }, 1537 { a: 0n, b: 10n, num_steps: 11, result: [0n, 0n, 0n, 0n, 1n, 2n, 3n, 4n, 6n, 8n, 10n] }, 1538 { a: 10n, b: 0n, num_steps: 11, result: [10n, 10n, 10n, 10n, 9n, 8n, 7n, 6n, 4n, 2n, 0n] }, 1539 { a: 0n, b: 1000n, num_steps: 11, result: [0n, 9n, 39n, 89n, 159n, 249n, 359n, 489n, 639n, 809n, 1000n] }, 1540 { a: 1000n, b: 0n, num_steps: 11, result: [1000n, 991n, 961n, 911n, 841n, 751n, 641n, 511n, 361n, 191n, 0n] }, 1541 { a: 1n, b: 5n, num_steps: 5, result: [1n, 1n, 2n, 3n, 5n] }, 1542 { a: 5n, b: 1n, num_steps: 5, result: [5n, 5n, 4n, 3n, 1n] }, 1543 { a: 0n, b: 10n, num_steps: 5, result: [0n, 0n, 2n, 5n, 10n] }, 1544 { a: 10n, b: 0n, num_steps: 5, result: [10n, 10n, 8n, 5n, 0n] }, 1545 { a: -10n, b: 10n, num_steps: 11, result: [-10n, -10n, -10n, -10n, -8n, -6n, -4n, -2n, 2n, 6n, 10n] }, 1546 { a: 10n, b: -10n, num_steps: 11, result: [10n, 10n, 10n, 10n, 8n, 6n, 4n, 2n, -2n, -6n, -10n] }, 1547 { a: -10n, b: 0n, num_steps: 11, result: [-10n, -10n, -10n, -10n, -9n, -8n, -7n, -6n, -4n, -2n, -0n] }, 1548 { a: 0n, b: -10n, num_steps: 11, result: [0n, 0n, 0n, 0n, -1n, -2n, -3n, -4n, -6n, -8n, -10n] }, 1549 ] 1550 ) 1551 .fn(test => { 1552 const a = test.params.a; 1553 const b = test.params.b; 1554 const num_steps = test.params.num_steps; 1555 const got = biasedRangeBigInt(a, b, num_steps); 1556 const expect = test.params.result; 1557 1558 test.expect( 1559 objectEquals(got, expect), 1560 `biasedRangeBigInt(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}` 1561 ); 1562 }); 1563 1564 interface fullF32RangeCase { 1565 neg_norm: number; 1566 neg_sub: number; 1567 pos_sub: number; 1568 pos_norm: number; 1569 expect: Array<number>; 1570 } 1571 1572 g.test('fullF32Range') 1573 .paramsSimple<fullF32RangeCase>( 1574 // prettier-ignore 1575 [ 1576 { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ -0.0, 0.0 ] }, 1577 { neg_norm: 1, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.min, -0.0, 0.0] }, 1578 { neg_norm: 2, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.min, kValue.f32.negative.max, -0.0, 0.0 ] }, 1579 { neg_norm: 3, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.min, -1.9999998807907104, kValue.f32.negative.max, -0.0, 0.0 ] }, 1580 { neg_norm: 0, neg_sub: 1, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.subnormal.min, -0.0, 0.0 ] }, 1581 { neg_norm: 0, neg_sub: 2, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.subnormal.min, kValue.f32.negative.subnormal.max, -0.0, 0.0 ] }, 1582 { neg_norm: 0, neg_sub: 0, pos_sub: 1, pos_norm: 0, expect: [ -0.0, 0.0, kValue.f32.positive.subnormal.min ] }, 1583 { neg_norm: 0, neg_sub: 0, pos_sub: 2, pos_norm: 0, expect: [ -0.0, 0.0, kValue.f32.positive.subnormal.min, kValue.f32.positive.subnormal.max ] }, 1584 { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 1, expect: [ -0.0, 0.0, kValue.f32.positive.min ] }, 1585 { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 2, expect: [ -0.0, 0.0, kValue.f32.positive.min, kValue.f32.positive.max ] }, 1586 { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 3, expect: [ -0.0, 0.0, kValue.f32.positive.min, 1.9999998807907104, kValue.f32.positive.max ] }, 1587 { neg_norm: 1, neg_sub: 1, pos_sub: 1, pos_norm: 1, expect: [ kValue.f32.negative.min, kValue.f32.negative.subnormal.min, -0.0, 0.0, kValue.f32.positive.subnormal.min, kValue.f32.positive.min ] }, 1588 { neg_norm: 2, neg_sub: 2, pos_sub: 2, pos_norm: 2, expect: [ kValue.f32.negative.min, kValue.f32.negative.max, kValue.f32.negative.subnormal.min, kValue.f32.negative.subnormal.max, -0.0, 0.0, kValue.f32.positive.subnormal.min, kValue.f32.positive.subnormal.max, kValue.f32.positive.min, kValue.f32.positive.max ] }, 1589 ] 1590 ) 1591 .fn(test => { 1592 const neg_norm = test.params.neg_norm; 1593 const neg_sub = test.params.neg_sub; 1594 const pos_sub = test.params.pos_sub; 1595 const pos_norm = test.params.pos_norm; 1596 const got = scalarF32Range({ neg_norm, neg_sub, pos_sub, pos_norm }); 1597 const expect = test.params.expect; 1598 1599 test.expect( 1600 compareArrayOfNumbersF32(got, expect, 'no-flush'), 1601 `fullF32Range(${neg_norm}, ${neg_sub}, ${pos_sub}, ${pos_norm}) returned [${got}]. Expected [${expect}]` 1602 ); 1603 }); 1604 1605 interface fullF16RangeCase { 1606 neg_norm: number; 1607 neg_sub: number; 1608 pos_sub: number; 1609 pos_norm: number; 1610 expect: Array<number>; 1611 } 1612 1613 g.test('fullF16Range') 1614 .paramsSimple<fullF16RangeCase>( 1615 // prettier-ignore 1616 [ 1617 { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ -0.0, 0.0 ] }, 1618 { neg_norm: 1, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.min, -0.0, 0.0] }, 1619 { neg_norm: 2, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.min, kValue.f16.negative.max, -0.0, 0.0 ] }, 1620 { neg_norm: 3, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.min, -1.9990234375, kValue.f16.negative.max, -0.0, 0.0 ] }, 1621 { neg_norm: 0, neg_sub: 1, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.subnormal.min, -0.0, 0.0 ] }, 1622 { neg_norm: 0, neg_sub: 2, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.subnormal.min, kValue.f16.negative.subnormal.max, -0.0, 0.0 ] }, 1623 { neg_norm: 0, neg_sub: 0, pos_sub: 1, pos_norm: 0, expect: [ -0.0, 0.0, kValue.f16.positive.subnormal.min ] }, 1624 { neg_norm: 0, neg_sub: 0, pos_sub: 2, pos_norm: 0, expect: [ -0.0, 0.0, kValue.f16.positive.subnormal.min, kValue.f16.positive.subnormal.max ] }, 1625 { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 1, expect: [ -0.0, 0.0, kValue.f16.positive.min ] }, 1626 { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 2, expect: [ -0.0, 0.0, kValue.f16.positive.min, kValue.f16.positive.max ] }, 1627 { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 3, expect: [ -0.0, 0.0, kValue.f16.positive.min, 1.9990234375, kValue.f16.positive.max ] }, 1628 { neg_norm: 1, neg_sub: 1, pos_sub: 1, pos_norm: 1, expect: [ kValue.f16.negative.min, kValue.f16.negative.subnormal.min, -0.0, 0.0, kValue.f16.positive.subnormal.min, kValue.f16.positive.min ] }, 1629 { neg_norm: 2, neg_sub: 2, pos_sub: 2, pos_norm: 2, expect: [ kValue.f16.negative.min, kValue.f16.negative.max, kValue.f16.negative.subnormal.min, kValue.f16.negative.subnormal.max, -0.0, 0.0, kValue.f16.positive.subnormal.min, kValue.f16.positive.subnormal.max, kValue.f16.positive.min, kValue.f16.positive.max ] }, 1630 ] 1631 ) 1632 .fn(test => { 1633 const neg_norm = test.params.neg_norm; 1634 const neg_sub = test.params.neg_sub; 1635 const pos_sub = test.params.pos_sub; 1636 const pos_norm = test.params.pos_norm; 1637 const got = scalarF16Range({ neg_norm, neg_sub, pos_sub, pos_norm }); 1638 const expect = test.params.expect; 1639 1640 test.expect( 1641 compareArrayOfNumbersF32(got, expect), 1642 `fullF16Range(${neg_norm}, ${neg_sub}, ${pos_sub}, ${pos_norm}) returned [${got}]. Expected [${expect}]` 1643 ); 1644 }); 1645 1646 interface fullI32RangeCase { 1647 neg_count: number; 1648 pos_count: number; 1649 expect: Array<number>; 1650 } 1651 1652 g.test('fullI32Range') 1653 .paramsSimple<fullI32RangeCase>( 1654 // prettier-ignore 1655 [ 1656 { neg_count: 0, pos_count: 0, expect: [0] }, 1657 { neg_count: 1, pos_count: 0, expect: [kValue.i32.negative.min, 0] }, 1658 { neg_count: 2, pos_count: 0, expect: [kValue.i32.negative.min, -1, 0] }, 1659 { neg_count: 3, pos_count: 0, expect: [kValue.i32.negative.min, -1610612736, -1, 0] }, 1660 { neg_count: 0, pos_count: 1, expect: [0, 1] }, 1661 { neg_count: 0, pos_count: 2, expect: [0, 1, kValue.i32.positive.max] }, 1662 { neg_count: 0, pos_count: 3, expect: [0, 1, 536870912, kValue.i32.positive.max] }, 1663 { neg_count: 1, pos_count: 1, expect: [kValue.i32.negative.min, 0, 1] }, 1664 { neg_count: 2, pos_count: 2, expect: [kValue.i32.negative.min, -1, 0, 1, kValue.i32.positive.max ] }, 1665 ] 1666 ) 1667 .fn(test => { 1668 const neg_count = test.params.neg_count; 1669 const pos_count = test.params.pos_count; 1670 const got = fullI32Range({ negative: neg_count, positive: pos_count }); 1671 const expect = test.params.expect; 1672 1673 test.expect( 1674 compareArrayOfNumbersF32(got, expect), 1675 `fullI32Range(${neg_count}, ${pos_count}) returned [${got}]. Expected [${expect}]` 1676 ); 1677 }); 1678 1679 interface limitsBigIntBitsF64Case { 1680 bits: bigint; 1681 value: number; 1682 } 1683 1684 // Test to confirm kBit and kValue constants are equivalent for f64 1685 g.test('f64LimitsEquivalency') 1686 .paramsSimple<limitsBigIntBitsF64Case>([ 1687 { bits: kBit.f64.positive.max, value: kValue.f64.positive.max }, 1688 { bits: kBit.f64.positive.min, value: kValue.f64.positive.min }, 1689 { bits: kBit.f64.positive.nearest_max, value: kValue.f64.positive.nearest_max }, 1690 { bits: kBit.f64.positive.less_than_one, value: kValue.f64.positive.less_than_one }, 1691 { bits: kBit.f64.positive.pi.whole, value: kValue.f64.positive.pi.whole }, 1692 { bits: kBit.f64.positive.pi.three_quarters, value: kValue.f64.positive.pi.three_quarters }, 1693 { bits: kBit.f64.positive.pi.half, value: kValue.f64.positive.pi.half }, 1694 { bits: kBit.f64.positive.pi.third, value: kValue.f64.positive.pi.third }, 1695 { bits: kBit.f64.positive.pi.quarter, value: kValue.f64.positive.pi.quarter }, 1696 { bits: kBit.f64.positive.pi.sixth, value: kValue.f64.positive.pi.sixth }, 1697 { bits: kBit.f64.positive.e, value: kValue.f64.positive.e }, 1698 { bits: kBit.f64.max_ulp, value: kValue.f64.max_ulp }, 1699 { bits: kBit.f64.negative.max, value: kValue.f64.negative.max }, 1700 { bits: kBit.f64.negative.min, value: kValue.f64.negative.min }, 1701 { bits: kBit.f64.negative.nearest_min, value: kValue.f64.negative.nearest_min }, 1702 { bits: kBit.f64.negative.pi.whole, value: kValue.f64.negative.pi.whole }, 1703 { bits: kBit.f64.negative.pi.three_quarters, value: kValue.f64.negative.pi.three_quarters }, 1704 { bits: kBit.f64.negative.pi.half, value: kValue.f64.negative.pi.half }, 1705 { bits: kBit.f64.negative.pi.third, value: kValue.f64.negative.pi.third }, 1706 { bits: kBit.f64.negative.pi.quarter, value: kValue.f64.negative.pi.quarter }, 1707 { bits: kBit.f64.negative.pi.sixth, value: kValue.f64.negative.pi.sixth }, 1708 { bits: kBit.f64.positive.subnormal.max, value: kValue.f64.positive.subnormal.max }, 1709 { bits: kBit.f64.positive.subnormal.min, value: kValue.f64.positive.subnormal.min }, 1710 { bits: kBit.f64.negative.subnormal.max, value: kValue.f64.negative.subnormal.max }, 1711 { bits: kBit.f64.negative.subnormal.min, value: kValue.f64.negative.subnormal.min }, 1712 { bits: kBit.f64.positive.infinity, value: kValue.f64.positive.infinity }, 1713 { bits: kBit.f64.negative.infinity, value: kValue.f64.negative.infinity }, 1714 ]) 1715 .fn(test => { 1716 const bits = test.params.bits; 1717 const value = test.params.value; 1718 1719 const val_to_bits = bits === float64ToUint64(value); 1720 const bits_to_val = value === uint64ToFloat64(bits); 1721 test.expect( 1722 val_to_bits && bits_to_val, 1723 `bits = ${bits}, value = ${value}, returned val_to_bits as ${val_to_bits}, and bits_to_val as ${bits_to_val}, they are expected to be equivalent` 1724 ); 1725 }); 1726 1727 interface limitsNumberBitsCase { 1728 bits: number; 1729 value: number; 1730 } 1731 1732 // Test to confirm kBit and kValue constants are equivalent for f32 1733 g.test('f32LimitsEquivalency') 1734 .paramsSimple<limitsNumberBitsCase>([ 1735 { bits: kBit.f32.positive.max, value: kValue.f32.positive.max }, 1736 { bits: kBit.f32.positive.min, value: kValue.f32.positive.min }, 1737 { bits: kBit.f32.positive.nearest_max, value: kValue.f32.positive.nearest_max }, 1738 { bits: kBit.f32.positive.less_than_one, value: kValue.f32.positive.less_than_one }, 1739 { bits: kBit.f32.positive.pi.whole, value: kValue.f32.positive.pi.whole }, 1740 { bits: kBit.f32.positive.pi.three_quarters, value: kValue.f32.positive.pi.three_quarters }, 1741 { bits: kBit.f32.positive.pi.half, value: kValue.f32.positive.pi.half }, 1742 { bits: kBit.f32.positive.pi.third, value: kValue.f32.positive.pi.third }, 1743 { bits: kBit.f32.positive.pi.quarter, value: kValue.f32.positive.pi.quarter }, 1744 { bits: kBit.f32.positive.pi.sixth, value: kValue.f32.positive.pi.sixth }, 1745 { bits: kBit.f32.positive.e, value: kValue.f32.positive.e }, 1746 { bits: kBit.f32.max_ulp, value: kValue.f32.max_ulp }, 1747 { bits: kBit.f32.negative.max, value: kValue.f32.negative.max }, 1748 { bits: kBit.f32.negative.min, value: kValue.f32.negative.min }, 1749 { bits: kBit.f32.negative.nearest_min, value: kValue.f32.negative.nearest_min }, 1750 { bits: kBit.f32.negative.pi.whole, value: kValue.f32.negative.pi.whole }, 1751 { bits: kBit.f32.negative.pi.three_quarters, value: kValue.f32.negative.pi.three_quarters }, 1752 { bits: kBit.f32.negative.pi.half, value: kValue.f32.negative.pi.half }, 1753 { bits: kBit.f32.negative.pi.third, value: kValue.f32.negative.pi.third }, 1754 { bits: kBit.f32.negative.pi.quarter, value: kValue.f32.negative.pi.quarter }, 1755 { bits: kBit.f32.negative.pi.sixth, value: kValue.f32.negative.pi.sixth }, 1756 { bits: kBit.f32.positive.subnormal.max, value: kValue.f32.positive.subnormal.max }, 1757 { bits: kBit.f32.positive.subnormal.min, value: kValue.f32.positive.subnormal.min }, 1758 { bits: kBit.f32.negative.subnormal.max, value: kValue.f32.negative.subnormal.max }, 1759 { bits: kBit.f32.negative.subnormal.min, value: kValue.f32.negative.subnormal.min }, 1760 { bits: kBit.f32.positive.infinity, value: kValue.f32.positive.infinity }, 1761 { bits: kBit.f32.negative.infinity, value: kValue.f32.negative.infinity }, 1762 ]) 1763 .fn(test => { 1764 const bits = test.params.bits; 1765 const value = test.params.value; 1766 1767 const val_to_bits = bits === float32ToUint32(value); 1768 const bits_to_val = value === uint32ToFloat32(bits); 1769 test.expect( 1770 val_to_bits && bits_to_val, 1771 `bits = ${bits}, value = ${value}, returned val_to_bits as ${val_to_bits}, and bits_to_val as ${bits_to_val}, they are expected to be equivalent` 1772 ); 1773 }); 1774 1775 // Test to confirm kBit and kValue constants are equivalent for f16 1776 g.test('f16LimitsEquivalency') 1777 .paramsSimple<limitsNumberBitsCase>([ 1778 { bits: kBit.f16.positive.max, value: kValue.f16.positive.max }, 1779 { bits: kBit.f16.positive.min, value: kValue.f16.positive.min }, 1780 { bits: kBit.f16.positive.nearest_max, value: kValue.f16.positive.nearest_max }, 1781 { bits: kBit.f16.positive.less_than_one, value: kValue.f16.positive.less_than_one }, 1782 { bits: kBit.f16.positive.pi.whole, value: kValue.f16.positive.pi.whole }, 1783 { bits: kBit.f16.positive.pi.three_quarters, value: kValue.f16.positive.pi.three_quarters }, 1784 { bits: kBit.f16.positive.pi.half, value: kValue.f16.positive.pi.half }, 1785 { bits: kBit.f16.positive.pi.third, value: kValue.f16.positive.pi.third }, 1786 { bits: kBit.f16.positive.pi.quarter, value: kValue.f16.positive.pi.quarter }, 1787 { bits: kBit.f16.positive.pi.sixth, value: kValue.f16.positive.pi.sixth }, 1788 { bits: kBit.f16.positive.e, value: kValue.f16.positive.e }, 1789 { bits: kBit.f16.max_ulp, value: kValue.f16.max_ulp }, 1790 { bits: kBit.f16.negative.max, value: kValue.f16.negative.max }, 1791 { bits: kBit.f16.negative.min, value: kValue.f16.negative.min }, 1792 { bits: kBit.f16.negative.nearest_min, value: kValue.f16.negative.nearest_min }, 1793 { bits: kBit.f16.negative.pi.whole, value: kValue.f16.negative.pi.whole }, 1794 { bits: kBit.f16.negative.pi.three_quarters, value: kValue.f16.negative.pi.three_quarters }, 1795 { bits: kBit.f16.negative.pi.half, value: kValue.f16.negative.pi.half }, 1796 { bits: kBit.f16.negative.pi.third, value: kValue.f16.negative.pi.third }, 1797 { bits: kBit.f16.negative.pi.quarter, value: kValue.f16.negative.pi.quarter }, 1798 { bits: kBit.f16.negative.pi.sixth, value: kValue.f16.negative.pi.sixth }, 1799 { bits: kBit.f16.positive.subnormal.max, value: kValue.f16.positive.subnormal.max }, 1800 { bits: kBit.f16.positive.subnormal.min, value: kValue.f16.positive.subnormal.min }, 1801 { bits: kBit.f16.negative.subnormal.max, value: kValue.f16.negative.subnormal.max }, 1802 { bits: kBit.f16.negative.subnormal.min, value: kValue.f16.negative.subnormal.min }, 1803 { bits: kBit.f16.positive.infinity, value: kValue.f16.positive.infinity }, 1804 { bits: kBit.f16.negative.infinity, value: kValue.f16.negative.infinity }, 1805 ]) 1806 .fn(test => { 1807 const bits = test.params.bits; 1808 const value = test.params.value; 1809 1810 const val_to_bits = bits === float16ToUint16(value); 1811 const bits_to_val = value === uint16ToFloat16(bits); 1812 test.expect( 1813 val_to_bits && bits_to_val, 1814 `bits = ${bits}, value = ${value}, returned val_to_bits as ${val_to_bits}, and bits_to_val as ${bits_to_val}, they are expected to be equivalent` 1815 ); 1816 }); 1817 1818 interface cartesianProductCase<T> { 1819 inputs: T[][]; 1820 result: T[][]; 1821 } 1822 1823 g.test('cartesianProductNumber') 1824 .paramsSimple<cartesianProductCase<number>>( 1825 // prettier-ignore 1826 [ 1827 { inputs: [[0], [1]], result: [[0, 1]] }, 1828 { inputs: [[0, 1], [2]], result: [[0, 2], 1829 [1, 2]] }, 1830 { inputs: [[0], [1, 2]], result: [[0, 1], 1831 [0, 2]] }, 1832 { inputs: [[0, 1], [2, 3]], result: [[0,2], 1833 [1, 2], 1834 [0, 3], 1835 [1, 3]] }, 1836 { inputs: [[0, 1, 2], [3, 4, 5]], result: [[0, 3], 1837 [1, 3], 1838 [2, 3], 1839 [0, 4], 1840 [1, 4], 1841 [2, 4], 1842 [0, 5], 1843 [1, 5], 1844 [2, 5]] }, 1845 { inputs: [[0, 1], [2, 3], [4, 5]], result: [[0, 2, 4], 1846 [1, 2, 4], 1847 [0, 3, 4], 1848 [1, 3, 4], 1849 [0, 2, 5], 1850 [1, 2, 5], 1851 [0, 3, 5], 1852 [1, 3, 5]] }, 1853 1854 ] 1855 ) 1856 .fn(test => { 1857 const inputs = test.params.inputs; 1858 const got = cartesianProduct(...inputs); 1859 const expect = test.params.result; 1860 1861 test.expect( 1862 objectEquals(got, expect), 1863 `cartesianProduct(${JSON.stringify(inputs)}) returned ${JSON.stringify( 1864 got 1865 )}. Expected ${JSON.stringify(expect)} ` 1866 ); 1867 }); 1868 1869 g.test('cartesianProductArray') 1870 .paramsSimple<cartesianProductCase<number[]>>( 1871 // prettier-ignore 1872 [ 1873 { inputs: [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], result: [[[0, 1], [4, 5]], 1874 [[2, 3], [4, 5]], 1875 [[0, 1], [6, 7]], 1876 [[2, 3], [6, 7]]]}, 1877 { inputs: [[[0, 1], [2, 3]], [[4, 5], [6, 7]], [[8, 9]]], result: [[[0, 1], [4, 5], [8, 9]], 1878 [[2, 3], [4, 5], [8, 9]], 1879 [[0, 1], [6, 7], [8, 9]], 1880 [[2, 3], [6, 7], [8, 9]]]}, 1881 { inputs: [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[2, 1, 0], [5, 4, 3], [8, 7, 6]]], result: [[[0, 1, 2], [2, 1, 0]], 1882 [[3, 4, 5], [2, 1, 0]], 1883 [[6, 7, 8], [2, 1, 0]], 1884 [[0, 1, 2], [5, 4, 3]], 1885 [[3, 4, 5], [5, 4, 3]], 1886 [[6, 7, 8], [5, 4, 3]], 1887 [[0, 1, 2], [8, 7, 6]], 1888 [[3, 4, 5], [8, 7, 6]], 1889 [[6, 7, 8], [8, 7, 6]]]} 1890 1891 ] 1892 ) 1893 .fn(test => { 1894 const inputs = test.params.inputs; 1895 const got = cartesianProduct(...inputs); 1896 const expect = test.params.result; 1897 1898 test.expect( 1899 objectEquals(got, expect), 1900 `cartesianProduct(${JSON.stringify(inputs)}) returned ${JSON.stringify( 1901 got 1902 )}. Expected ${JSON.stringify(expect)} ` 1903 ); 1904 }); 1905 1906 interface calculatePermutationsCase<T> { 1907 input: T[]; 1908 result: T[][]; 1909 } 1910 1911 g.test('calculatePermutations') 1912 .paramsSimple<calculatePermutationsCase<number>>( 1913 // prettier-ignore 1914 [ 1915 { input: [0, 1], result: [[0, 1], 1916 [1, 0]] }, 1917 { input: [0, 1, 2], result: [[0, 1, 2], 1918 [0, 2, 1], 1919 [1, 0, 2], 1920 [1, 2, 0], 1921 [2, 0, 1], 1922 [2, 1, 0]] }, 1923 { input: [0, 1, 2, 3], result: [[0, 1, 2, 3], 1924 [0, 1, 3, 2], 1925 [0, 2, 1, 3], 1926 [0, 2, 3, 1], 1927 [0, 3, 1, 2], 1928 [0, 3, 2, 1], 1929 [1, 0, 2, 3], 1930 [1, 0, 3, 2], 1931 [1, 2, 0, 3], 1932 [1, 2, 3, 0], 1933 [1, 3, 0, 2], 1934 [1, 3, 2, 0], 1935 [2, 0, 1, 3], 1936 [2, 0, 3, 1], 1937 [2, 1, 0, 3], 1938 [2, 1, 3, 0], 1939 [2, 3, 0, 1], 1940 [2, 3, 1, 0], 1941 [3, 0, 1, 2], 1942 [3, 0, 2, 1], 1943 [3, 1, 0, 2], 1944 [3, 1, 2, 0], 1945 [3, 2, 0, 1], 1946 [3, 2, 1, 0]] }, 1947 ] 1948 ) 1949 .fn(test => { 1950 const input = test.params.input; 1951 const got = calculatePermutations(input); 1952 const expect = test.params.result; 1953 1954 test.expect( 1955 objectEquals(got, expect), 1956 `calculatePermutations(${JSON.stringify(input)}) returned ${JSON.stringify( 1957 got 1958 )}. Expected ${JSON.stringify(expect)} ` 1959 ); 1960 });