Repository navigation
Handle sNaNs in {min,max}_precise on Neon - #441
RunDevelopment wants to merge 3 commits into
Conversation
|
There is a much more significant problem with these methods:
It's a much bigger issue for determinism in practice than the theoretical possibility of signalling NaNs. If you want fully IEEE compliant min/max that deals with all kinds of NaN and also signed zero, you need a much slower formulation. People have workshopped variants of it in this thread: https://news.ycombinator.com/item?id=49800085 I don't want to slow down the _precise variant for everyone because of signalling NaNs, but I am open to adding a min_ieee or some such that is fully deterministic at the cost of speed. |
|
My goal was to make them behave the same as the standard library by getting rid of the NaN caveat. Since the standard library doesn't care about non-deterministic +/-0, neither do I. IMO, implementing IEEE 754's
Plus, the compiler can optimize away |
Motivation
The
_precisefamily of methods is supposed to return platform-independent precise results at the cost of speed. So it was very surprising to me when I read thatmax_precisedidn't handle NaNs correctly. To quote the docs:In my case, the numbers I get are user input (they could be anything) so I can't just assume sNaNs aren't included. Plus, no other operation has this caveat around sNaNs and neither does the standard library with
fN::max.Changes
This PR removes the caveat around sNaN by bringing Neon in line. All other platforms already handled NaNs (signally or not) like the standard library.
To bring Neon in line, I'm using the same trick as the standard library.
vmaxnmq_f32is the element-wise max operation, and it also has the property of never returning sNaNs. Sovmaxnmq_f32(x, x)will return a quiet NaN if x is any NaN value (signalling or not) and the numeric value of x otherwise. With this trick, sNaN disappears.Example:
max_precise(a, b)is implemented like this:For consistency, I also adjusted
reduce_{max,min}_preciseto handle sNaN correctly using the same trick. However, the standard reduction from larger-than-native vectors would have resulted in a few unnecessary sNaN -> qNaN conversions. To avoid them, I also implemented a custom reduce logic to skip the unnecessary conversions.