3 ms·
My preference in tests is a little different than just using IEEE 754 ==, _Bool equiv(float x, float y) { return (x <= y && y <= x) ||
by mtklein 6mo ago
My preference in tests is a little different than just using IEEE 754 ==,
_Bool equiv(float x, float y) {
return (x <= y && y <= x)
|| (x != x && y != y);
}
which both handles NaNs sensibly (all NaNs are equivalent) and won't warn about using == on floats. I find it also easy to remember how to write when starting a new project.
- z_open 6mo agoWhy is that NaN handling sensible? I don't think it makes sense to say log(-1) equals log(-2). Mathematically it isn't true and your implementation would say it's true only because of limitations in IEEE754.
- xigoi 6mo agoMathematically it’s also not true that 2³⁰⁹ = +∞. You simply can’t expect correct results from floats.
- tengwar2 6mo agoCould you explain the bit about "all NaNs are equivalent"? IEEE requires that NaN ≠ NaN, but I suspect that I am just misunderstanding what you mean.
- mtklein 6mo agowhat I mean here about NaNs is that from a testing perspective, I want to be able to write a test that expects NaN in the same way that I write other expectations, and you can't do that with ==. assert(x == 7); // fine assert(y == NaN); // never true assert(y != y); // this is what you meant so this equiv() helper fixes that, assert(equiv(x, 7)); // fine assert(equiv(y, NaN)); // also fine now, as far as treating NaNs equivalently, the IEEE 754 float format has a huge number of possible representations of NaN, and if you did something like a bitwise comparison, you might think that 0x7fc00000, 0x7f800001, 0xffc00000, 0x7fc0f00d were all different and not equivalent, but they're all NaNs, and I find that when I'm looking for a NaN, I very rarely care about exactly which one I'm looking at. So checking (x!=x && y!=y) admits any two NaNs as equivalent.