You have a strongly ordered `NotNan` struct that wraps a float that's guaranteed to not be NaN, and an `OrderedFloat` that consideres all NaN equal, and greater than non-NaN values.
These are basically the special-cases you'd need to handle yourself anyway, and probably one of the approaches you'd end up taking.
I am not saying we do not need NaNs (I would even love to see them in integers, see: https://news.ycombinator.com/item?id=45174074), but I would prefer if we had less of them in floats with clear sorting rules.
How should an algorithm specify that it should sort by insertion order instead of memory address order if the sort key is NaN for multiple records?
That's the default in SQL Relational Algebra IIRC?
Well each programming language has a "sort" method that sorts arrays. Should this method throw an exception in case of NaN? I think the NaN rules were the wrong decision. Because of these rules, everywhere there are floating point numbers, the libraries have to have special code for NaN, even if they don't care about NaN. Otherwise there might be ugly bugs, like sorting running into endless loops, data loss, etc. But well, it can't be changed now.
The best description of the decision is probably [1], where Stephen Canon (former member of the IEEE-754 committee if I understand correctly) explains the reasoning.
[1] https://stackoverflow.com/questions/1565164/what-is-the-rati...
There's probably no good way to standardize how to fill when values are null or nan. How else could this be solved without adding special cases for NaN?
In a language with type annotations we indicate whether a type is Optional:
def sum(a: float|None, b: Optional[float]) -> None|float :
def sum(a: float|np.nan|None, b: Optional[float|np.nan]) -> None|float|np.nan :But NaN could be defined to be smaller or higher than any other value.
Well, there are multiple NaN. And NaN isn't actually the only weirdness; there's also -0, and we have -0 == 0. I think equality for floating point is anyway weird, so then why not just define -0 < 0.
1. You don't need to utter the keyword "unsafe" to implement these traits for your type. If you're not allowed by policy to write unsafe Rust (or if you just don't want to risk making any mistakes), you can implement these traits anyway. If you do that you should do so correctly as with writing any software...
2. But, because they're safe traits, nobody else's Rust software is allowed to rely on your correctness. If you disobeyed a rule, such as you decide all values of your type are always greater than themselves (whether carelessly or because you're a vandal) other Rust software mustn't become unsafe as a result.
3. This has real world implications, for example if your type Goose has an Ord implementation which is defective, whether on purpose or by mistake, sorting a Vec<Goose> in Rust won't have Undefined Behaviour like in C++, it might panic (in debug) and it can't necessarily sort your type if your Ord implementation is nonsensical, but the "sorted" Vec<Goose> is the same geese as your original, just potentially in a sorted state to the extent that meant anything. It's not fewer geese, or more geese, or just different geese altogether - and it certainly isn't say, an RCE now like it might be in C++