
On Dec 26, 2019, at 14:46, Marco Sulla via Python-ideas <python-ideas@python.org> wrote:
David Mertz wrote:
NaN is an IEEE-854 value, everything you mention is precisely identical of floats. Is your argument that we need to stop using the '<' operator for floats also?!
Nope, but I simply ignored in this case IEEE 754-2019 (that supersedes IEEE 854)
Not that this is relevant, but if you’re going to be a pedant, the floats in whatever Python you’re using right now are probably 854/754-1985 doubles, not 754-2019 binary64s.
This is because NaN, IMHO, it's not the right name for the entity. A number can't be Not a Number. It's an Undefined Number.
OK, so you really are arguing against IEEE float here. As David said, you can die on that hill if you want to. I think everyone agrees that it’s not an ideal design (except maybe fictional mathematicians as imagined by electrical engineers), but everyone has agreed to use it anyway. So if your argument is that IEEE float is wrong, and set is wrong for the same reason, and therefore dict shouldn’t be like set, that’s fine, but don’t expect many people to be sold.
But IEEE 754-2019 is written mainly for C and Assembly. And you can't return nothing in a comparison between two floats. So they take the most practical solution: return false.
They defined a whole complicated system of signaling vs. quiet nan values and signaling (on sNaN) vs. non-signaling operations and multiple forms of exception handling from traps to checkable flags to substitution of default values. You can use much of that in C (and all of it in assembly) if you want to, even if people usually don’t. Also, who says you can’t return nothing in a comparison between two floats? You can write a named function that has any API you want (say, it returns -1, 0, 1, or NaN, or even an enum of less, greater, equal, left nan, right nan, both nan, other incomparable) instead of a Boolean function that has to return true or false. But the fact that C (and countless other languages, including Python) also provides the operators and people use them extensively implies that many people don’t think the operators are useless, even if you do.
This obviously does not apply to total ordering. Even if I think mathematically has no sense, IEEE 754 clearly defined a total ordering, also for NaNs. So a total_ordering() key function, passed to sort functions, should order as IEEE 754 declares, without raising exceptions. There's a bug opened somewhere about this key function.
This is what I've done. Unluckily, the API now works this way. It's not a big problem anyway. You have only to pass as key to the sort function:
``` def sortNans(x): try: if math.isnan(x): return float("+inf") except TypeError pass
return x ```
But this isn’t the same thing that IEEE totalOrder defines. The biggest difference is that it treats nan the same as inf, instead of larger—since the original argument revolved around putting nans at the end so that, e.g., programmers can lop the nans off, this seems pretty significant. It also doesn’t distinguish -0 and 0, different representations of other equal doubles, negative NaNs, sNaNs, and NaNs with different payloads—all of which are increasingly unlikely to be relevant, especially compared to nan vs. inf, but if you’re going to complain that Python doesn’t do IEEE totalOrder you shouldn’t offer a fix that still doesn’t.
and do
sorted(iterable_with_nans, key=sortNans)
This key does not distinguish between NaN and -NaN... but who cares!
Presumably whoever is asking for IEEE totalOrder instead of IEEE < cares, or why would they be asking for it? Of course if you want something different from IEEE comparison and also different from IEEE totalOrder for some use case, that’s fine—you just showed how trivial it is to do whatever you want, with the current API. I don’t see how that demonstrates that the API is “unlucky”; It seems like the exact opposite. If you want to move NaNs to the end, or treat them the same as inf, or raise, or anything else you can think of, it’s easy. I can’t see how any one-size-fits-all API could make more than one of those things easy.