5 ms·
> Because one of the basic axioms of equivalence relations (which equality should certainly be) is reflexivity; anything should be equal to itself, and other co
by thwarted 5y ago
> Because one of the basic axioms of equivalence relations (which equality should certainly be) is reflexivity; anything should be equal to itself, and other code will break if it isn't (e.g. in many languages you can corrupt a binary tree structure by inserting multiple NaNs into it, because they'll both compare less than each other).
NaN is not something that can be pointed to. NaN doesn't exist like numbers exist in relation to each other, it's a concept (or at least more conceptual than numbers themselves are conceptual). NaN doesn't exist on a number line, so it makes no sense to order, compare for greater or less than, a NAN with numbers or anything else. It literally means "this doesn't exist". It doesn't have a "self" for reflexivity to apply to. A binary tree's nodes need to be comparable and orderable, and NaN is not comparable to anything. There is no place to put a NaN in a binary tree, nor is there really a use case to "find a NaN quickly" that a binary tree would be useful for (what would the utility be of a binary tree where the keys are all NaN?). NaN is a sentinel, and it exists outside the set of floats, despite being a float itself for type purposes, which is why it needs to be tested for using specific operators (isNaN) and not using equality operators. Part of the utility of NaN is that it can not be tested for using equality.
Of course, computers are limited in what can be represented using bits, so NaN does have a specific (set of) binary representation(s), and you could work with those, but that avoids the useful abstraction NaN is mean to represent.
- lmm 5y ago> NaN is a sentinel, and it exists outside the set of floats, despite being a float itself for type purposes, which is why it needs to be tested for using specific operators (isNaN) and not using equality operators. Part of the utility of NaN is that it can not be tested for using equality. That seems like pretty bad type design - if you want a sum type, define an actual sum type. If a given type doesn't have a well-behaved concept of equality or comparison (and to be clear, that means well behaved for any values of that type), it shouldn't support the standard equality or comparison operators. I agree that you probably don't want to put NaN in a binary tree, but the language should support you in not doing that rather than silently breaking when you do.
- kevin_thibedeau 5y agoThe whole point of NaN working this way is that it is not well behaved and raises an alarm when a bad computation is made. Otherwise you run the risk of wiping out a NaN and never knowing that a divide by zero has corrupted your results.
- lmm 5y agoHaving it not compare equal to itself is a pretty crappy way of making it raise an alarm - you're relying on that accidentally causing some kind of visible corruption to your program's datastructures, which is not at all guaranteed. Anyone who cares about not silently getting corrupt results should be turning on floating-point exceptions rather than using NaNs, unless they know exactly what they're doing.
- chrismorgan 5y agoNaN as an approach to handling arithmetic errors only makes sense (weighed against checked arithmetic) if it poisons all computations involving it. I would argue firstly that floating-point numbers should not implement float == float → boolean comparison, because downgrading NaN to a boolean loses its infectious nature, undermining the purpose of NaN as an error-handling (or at least -detection) scheme. But if you’re going to implement float == float → boolean comparison, I would argue that NaN not equalling itself is more reasonable on this axis of poisoning all computations than the alternative, though I will admit that it’s rendered subjective by the fact that there is no inherent virtue in equality (your “normal” path could just as easily be conditional on inequality). I’d really like to see more experimentation into languages that only support checked arithmetic (not just for floating-point numbers, but also for integer types).
- thwarted 5y agoYou seem to be equating having a NaN in a result as being undetectable corruption. The whole point of NaN is that you don't end up with silent data corruption, you end up with an unusable value: unusable in the sense that it infects further calculations instead of covering it up with a value that looks valid. r = (x / y) + 1 If y was 0, I wouldn't want r to equal 1; if r was 1 (or anything other than NaN, really) then that would be silent data corruption. That some languages have a literal form of NaN that can be typed into code and that has a type that is the same as the type of floating point numbers leads one to believe that an equality operator can/should be used to test for NaN. Really, a literal form of NaN is kind of a blight on such a language: the only way to obtain a value that is NaN should be via a calculation that results in it, there is very little need for a literal form, except for the case where you want to store the result of a calculation that might be NaN (to record that the calculation was corrupted). But in the wide range of cases, if someone was typing a literal NaN into their code, that would be a code smell: NaN is a legit result of a calculation, but not a legit input to a calculation because it infects any calculation it touches, and since there's not a legit reason to have it as an input, there's no reason to have a literal form that can be typed into the code. As for floating point exceptions vs testing for NaN: x = performcalculation() if isNan(x) { abort } y = performFurtherCalc(x) if isNan(y) { abort } This can be done at every point an additional value is introduced to the calculation, to determine if the result is sane or not. Floating point exceptions do these checks for you.