3 ms·
It is not an IEEE 754 oddity. It is the correct mathematical behavior. When you define an order relation on a set, the order may be either a total order or a p
by adrian_b 8mo ago
It is not an IEEE 754 oddity. It is the correct mathematical behavior.
When you define an order relation on a set, the order may be either a total order or a partial order.
In a totally ordered set, there are 3 values for a comparison operation: equal, less and greater. In a partially ordered set, there are 4 values for a comparison operation: equal, less, greater and unordered.
For a totally ordered set you can define 6 relational operators (6 = 2^3 - 2, where you subtract 2 for the always false and always true predicates), while for a partially ordered set you can define 14 relational operators (14 = 2^4 - 2).
For some weird reason, many programmers have not been taught properly about partially-ordered sets and also most programming languages do not define the 14 relational operators needed for partially ordered sets, but only the 6 relational operators that are sufficient for a totally ordered set.
It is easy to write all 14 relational operators by combinations of the symbols for not, less, greater and equal, so parsing this in a programming language would be easy.
This lack of awareness about partial order relations and the lack of support in most programming languages is very bad, because practical applications need very frequently partial orders instead of total orders.
For the floating-point numbers, the IEEE standard specifies 2 choices. You can either use them as a totally-ordered set, or as a partially-ordered set.
When you encounter NaNs as a programmer, that is because you have made the choice to have partially-ordered FP numbers, so you are not allowed to complain that this is an odd behavior, when you have chosen it. Most programmers do not make this choice consciously, because they just use the default configuration of the standard library, but it is still their fault if the default does not do what they like, but nonetheless they have not changed the default settings.
If you do not want NaNs, you must not mask the invalid operation exception. This is actually what the IEEE standard recommends as the default behavior, but lazy programmers do not want to handle exceptions, so most libraries choose to mask all exceptions in their default configurations.
When invalid operations generate exceptions, there are no NaNs and the FP numbers are totally ordered, so the 6 relational operators behave as naive programmers expect them to behave.
If you do not want to handle the invalid operation exception and you mask it, there is no other option for the CPU than to use a special value that reports an invalid operation, and which is indeed not-a-number. With not-numbers added to the set of FP numbers, the set becomes a partially-ordered set and all relational operators must be interpreted accordingly.
If you use something like C/C++, with only 6 relational operators, then you must do before any comparison tests to detect any NaN operand, because otherwise the relational operators do not do what you expect them to do.
In a language with 14 relational operators, you do not need to check for NaNs, but you must choose carefully the relational operator, because for a partially-ordered set, for example not-less is not the same with greater-or-equal (because not-less is the same with greater-or-equal-or-unordered).
If you do not expect to do invalid operations frequently, it may be simpler to unmask the exception, so that you will never have to do any test for NaN detection.
- AndriyKunitsyn 8mo ago>With not-numbers added to the set of FP numbers, the set becomes a partially-ordered set and all relational operators must be interpreted accordingly. The same not-number, produced by the same computation, occupying the same memory, is still not equal to itself. It is true that I haven't been able to brush up my knowledge on partial ordering, but isn't being identical is the same as being equal in math?
- adrian_b 8mo agoIf you test if a not-a-number is equal to itself, the result is false. If you test if a not-a-number is not equal to itself, the result is also false. The reason is that the result of comparing a not-number with itself is neither "equal" nor "not equal", but "unordered". This should make perfect intuitive sense, because a NaN encodes the fact that "an undefined operation has happened". For instance, you have 2 instances of the same NaN, both having been obtained by multiplying zero with infinity. However it may happen that one was obtained while computing values of a sequence that converges to 10 and the other may have been obtained by computing values of a sequence that converges to 100. Then there is no doubt that equality is not true for this 2 NaNs. However, those 2 NaNs may have been generated while computing values of sequences that both converge to 10, which shows that neither non-equality may be trued for these 2 NaNs. When you have NaNs, that means that it is unknown which value, if any, the invalid operations should have produced. When comparing 2 unknown values, you cannot know whether they are equal or not equal, so both testing for equality and for non-equality must fail.
- AndriyKunitsyn 8mo agoOkay, but how could the fact that these elements are in a partially ordered set, or whatever set, trump the basic law of logic, the law of identity, "a = a"? Or the argument is that NaNs are not actually the values themselves, but the representations of the facts of different failures, and because we can't compare the facts, we shouldn't compare NaNs? Well, I guess one could say that numbers in general are also such incomplete representations; 2 is 2, I could get 2 by adding one crayon to another crayon, or by taking 10 crayons and removing 8 of them. That doesn't stop me from comparing these 2s.