Several threads here have discussed how `f64`

doesn't implement `Eq`

or `Ord`

or `Hash`

because of NaN values. This is indeed unpleasant.

I think of NaNs as a floating point arithmetic design mistake, similar to having `null`

values by default in reference types, which Tony Hoare has called a "billion dollar mistake".

But here is another problem with NaNs. Consider this innocent-looking code:

```
println!("{}", f64::sqrt(1001.3 - 1001.0 - 0.3));
```

You would think it would print something in the vicinity of the correct answer, 0.0, maybe a bit off due to rounding errors.

What does it actually print? NaN.

That's because due to rounding errors it ends up taking a square root of a tiny negative number, and sqrt is defined to return NaN for negative numbers.

Therefore really, instead of `f64::sqrt(x)`

, you should always do `f64::sqrt(x.max(0.0))`

, if you want to be robust to rounding errors.

I think it would be a lot better if `sqrt`

was simply defined to be 0 for negative numbers. Unfortunately the IEEE-754 standard chose this to be NaN instead.

Similarly, `f64::acos(1.00001)`

is NaN. You should really do `f64::acos(x.clamp(-1.0, 1.0))`

if you want to be robust.

Same thing for `f64::asin`

, `f64::atan`

, `f64::log`

, etc. You really have to clamp the input to the correct range if you want them to work robustly in the presence of rounding errors.

I can think of only a few scenarios where there is no clear better alternative to NaN: 0 / 0, 0 * ∞, ∞ - ∞. It's not clear what they should return.

But, I think, it doesn't really matter very much what they return. If your code is robust to rounding errors, it has to be prepared for any answer to 0 / 0. If it's 0 / 0 precisely, you get NaN. But if it's 1e-300 / 0, you get infinity. If it's 1e-300 / 1e-300, you get 1.0. Etc. This is just a very unstable calculation.

So I think NaN might make sense for a few special cases like this, but not for other cases like sqrt(negative) or log(negative).

Since these special cases are only a few cases, and your code usually has to be prepared to get any answer from them anyway, it wouldn't do much harm to have a floating point arithmetic that just returns an arbitrary non-NaN answer in those cases as well (such as 0 or infinity), and get rid of NaNs altogether.

Also note that `1.0 / 0.0 = infinity`

is already quite arbitrary (it could be `-infinity`

). You could argue that it's `+0.0`

and not `-0.0`

, and `1.0 / (-0.0) = -infinity`

. But if we go that rabbit hole, then, `0.0 - 0.0 = +0.0`

is an arbitrary sign choice, why not `-0.0`

? So this business of dividing by zero is already quite arbitrary. So might as well make `+0.0 / +0.0 = infinity`

and `-0.0 / +0.0 = -infinity`

and it would be pretty consistent with all the other arbitrary choices about +0.0 and -0.0.