-
-
Notifications
You must be signed in to change notification settings - Fork 204
Description
π Bug Report
ECMAScript declares that all NaN values are indistinguishable from each other, but that the underlying bit pattern, which may be non-canonical, may be observable by writing to an ArrayBuffer depending on the engine.
I first noticed that float32Array() and float64Array() actually never seem to be able to produce non-canonical NaN bit patterns because they are derived from lists of floats/doubles and those are also always generated as canonical.
I think it would be better if at least float32Array() and float64Array() could produce any sequence of underlying bytes (e.g. maybe by being derived from an arbitrary ArrayBuffer). This would help with testing things that are supposed to roundtrip data perfectly.
I'm less opinionated about float() and double() producing non-canonical NaNs as well, because those differences might be really hard to observe in practice (only when writing to an ArrayBuffer before/without doing operations that happen to canonicalize the NaN), but it might still be worth it for catching those edge cases.
To Reproduce
Steps to reproduce:
const canonicalNaNBytes = new Uint8Array(new Float32Array([Number.NaN]).buffer)
fc.sample(
fc
.float32Array()
// Filter for Float32Arrays with non-canonical NaNs.
.filter(array => {
const nanIndex = array.findIndex(value => Number.isNaN(value))
if (nanIndex === -1) {
return false
}
const nanBytes = new Uint8Array(
array.slice(nanIndex, nanIndex + 1).buffer,
)
for (let i = 0; i < nanBytes.length; i++) {
if (nanBytes[i] !== canonicalNaNBytes[i]) {
// Found a non-canonical NaN!
return true
}
}
return false
}),
10,
)Expected behavior
The sampling should terminate by finding something, but it never finds a matching value.
Your environment
| Packages / Softwares | Version(s) |
|---|---|
| fast-check | 4.5.3 |
| node | v24.8.0 |
| TypeScript* | 5.9.3 |
*Only for TypeScript's users