7 ms·
Why is NaN not equal to NaN?
- tannerc 12y agoThe comments give reason to believe this isn't the cause, but my first instinct was that NaN cannot be equal to NaN as it isn't clear what the object represents. As Mike C states in his answer: "Another way to look at it is that NaN just gives you information about what something ISN'T, not what it is." But is this an effective way of looking at the question? In reality what you're trying to do is not compare a NaN to another NaN, you're actually attempting to check if both objects are NaN. This hurts my brain.
- karamazov 12y agoThis is what I would expect from the underlying mathematics. NaN roughly means the output of a function is undefined, and it does not make sense to say functions are equal at some value just because they are both undefined for that value. For example, it's probably more sensible to say that 2x/x != 3x/x for all values of x, rather than 2x/x != 3x/x for all x except 0, wherein both functions are undefined and therefore equal to each other.
- Fishkins 12y agoI agree it really doesn't make sense to say two NaNs are definitely equal, but it doesn't seem necessarily true that they aren't equal, either. Semantically, I would say NaN == NaN should return undefined (assuming JS here). Of course, it would be weird for == not to return true or false, so in practice that probably isn't a good idea. So I agree with you false is the least bad option.
- Sharlin 12y agoYes, you'd have to implement tri-state logic, with all its unintuitive implications, in all languages that wanted to use IEEE 754. Clearly not very feasible. In SQL, one of the few languages with tri-state boolean as a first-class data type, null==null does return null.
- Fishkins 12y agoYeah, SQL's handling of nulls tripped me up a couple times while I was getting used to the language.
- Dewie 12y ago> Semantically, I would say NaN == NaN should return undefined (assuming JS here). I thought that the philosophy of JS was to make meaning out of every execution and value, even the meaningless ones? Kind of the opposite of the fail-fast-and-hard philosophy. String index of NaN? JS will find a way.
- yourad_io 12y agoWhy wouldn't NaN==NaN return a NaN value, which also has the following property: -> Can't be used as a branching conditional. Think about it: if (NaN) { a; } else { b; } The pedantically-correct answer would be to execute neither, but that wouldn't be very helpful (however fun debugging that might have been). The pragmatically-correct answer, imho, would be to throw an error. Would I rather have a; or b; executed, when I can't trust the conditional value? Neither, by the time the NaN has hit branching code, it's time it escalated into an error. edit: I may have reinvented "Maybe" https://news.ycombinator.com/item?id=7747284 https://news.ycombinator.com/item?id=7747284
- Fishkins 12y agoI thought about that, but I like undefined better. Part of the reason is NaN is of type number[0], and I think a number makes less as a return value for a conditional statement than a general non-value (e.g. undefined or null). Also, if someone asked me to evaluate the equality of karamozov's equations at 0, I'd literally say "that's undefined." So the JS undefined value seemed perfect to me (perfect as in the most elegant, not necessarily the most practical). For some reason I hadn't thought about it till you mentioned it, but I do like the idea of throwing an exception when a nonsensical comparison of NaNs happen. Of course that would be inimical to the way JS normally operates. I don't think what you're getting at is exactly Maybe. You're able to easily programmatically determine whether a Maybe has a value, whereas the equality of NaNs is basically unknowable. 0 - Talking in JS again, although it's similar for Java and other languages.
- yourad_io 12y ago> You can ignore invalid data for as long as you want, but you can also draw lines and say "it can't have been ignored if flow gets to here". (from comment I linked above) I was talking about this property of Maybe. I was thinking that assigning a NaN value to a boolean should probably be "fine" (no exception), but using that when branching would mean that this carried-forward-error (which the NaN represents, effectively) is just about to go beyond "infecting" just data, to affecting code flow. So, throw.
- coherentpony 12y ago>This is what I would expect from the underlying mathematics. NaN roughly means the output of a function is undefined, That's not true, NaN means "not a number". Take for example f(x) = 1/x. What is f(0)? Infinity? Well, you can't divide by zero. In fact, I tricked you. When you talk about a function in mathematics, you need more information that just the formula, you also need to know over what domain I am allowed to evaluate such a function. What I should have said was, define f: [1, 2] -> R s.t. f(x) = 1/x. Now you are not allowed to ask the question, "what is f(0)?" because I didn't include zero in the domain. You might then ask, "so how do you define f at zero?" That question mentions 'f', but the 'f' it is referring to is now different. Such an 'f' you are allowed to ask this question for would be the following: f:[0, 2] -> R f(x) = 1/x when x > 0 f(0) = 1 Now, when you ask me what f(0) is, I can tell you. It is 1. "That's cheating!" you might add. No it isn't. It is a different function with different properties. Just because the formula is the same does not mean you can evaluate it wherever you want. Note: you are not allowed to divide by zero. The result would be meaningless if you could. And 'NaN' is only something the computer spits out when it encounters an operation that doesn't return a number. Usually, a computer will give you 'Inf' when you try to divide by zero numerically. Mathematically, you cannot do it. So don't try. NaN means 'not a number', and '=' is an infix operator to compare numbers. You can't compare for equality on things that are not numbers. You can compare infinities, however. "I thought you said dividing by zero was meaningless!" It is. I'm not going to divide by zero, but I can still talk about infinity. Take for example the following function: f: (0, 1] -> R f(x) = 1/x f is defined on every number strictly greater then zero, but less than or equal to one. Zero is not in the domain, so I can't evaluate f there. However, I can still study what happens near zero. Take a really small number bigger than zero. The reciprocal of that is huge. Now take a smaller number, the reciprocal becomes even bigger. In fact, we can keep doing this: construct any positive sequence that converges to zero (but never attains it). Now evaluate f at each element in that sequence, you will notice that the result becomes larger and larger. We call the limit of this 'infinity', and we have notation for this. Notice I did NOT evaluate f at zero, I merely studied what happens as I approach zero. Infinity is not a real number, and I cannot have f ever evaluate to infinity, but I can talk about limiting properties. Computers do this too, but they have a much lower standard for doing so. Essentially, anything bigger than the maximum value of a double (about 10^300, I think?) is called 'Inf' on a computer. Edit: I forgot to address the second paragraph of the parent comment. >For example, it's probably more sensible to say that 2x/x != 3x/x for all values of x, rather than 2x/x != 3x/x for all x except 0, wherein both functions are undefined and therefore equal to each other. This is not a sensible thing to do. Let me elaborate. I'm going to ignore any cancellation issue and say it is definitely true that 2/x != 3/x for x != 0. Let's also say that each of these functions has domain (0, 1]. These two functions are not equal are x = 0 because you cannot evaluate them at x = 0. It is forbidden. However, behaviour near to zero is very similar. See my discussion above. In fact, you can make a stronger statement. You can say that as x approaches zero (from the positive side), in the limit, 2/x and 3/x are equal. That is, they have the same limit as you approach zero. This does not make them equal at zero, because I cannot evaluate them at zero. To make this point absolutely clear, I will add zero into the domain and define two different functions: f:[0,1] -> R f(x) = 2/x for x > 0 f(0) = 1 and g:[0,1] -> R g(x) = 3/x for x > 0 g(0) = 2 These functions have the same limit as you approach zero, but they are not equal at zero. I can evaluate them at zero this time, because I defined what it meant to do so. And indeed f(0) = 1 != 2 = g(0).
- Dewie 12y agoThat seems pretty reasonable. What is > undefined == undefined In Haskell? Exception: Prelude [...]
- zenogais 12y agoThe following is my take: It seems like the desired result is to make it determinable when a NaN value is produced in a language that doesn't have an is_nan function (such as an assembly language lacking such a predicate) so that a simple boolean statement does the trick, for example: def is_nan(val): return not (val == val) This absolutely bizarre statement of non-identity gives us a surefire test. It is from the perspective of higher level languages that the non-identity of NaN with itself seems bizarre.
- dllthomas 12y agoIf NaN == NaN, we'd still have that test, it would just read def is_nan(val): return (val == NaN) It's true that it can't just give NaN or you'd be stuck.
- leeoniya 12y agotldr: cause !red !== !blue
- malkia 12y agoI like the fact that NaN taints other numbers - early failure is better, than trying to hide it. #include <math.h> #include <stdio.h> int main(int argc, const char* argv[] ) { double nan = 0.0 / 0.0; double x = 1.0; double y = nan + x; printf( "nan=%f\n", nan ); printf( " x=%f\n", x ); printf( " y=%f\n", y ); return 0; } /* Should print (gcc): nan=nan x=1.000000 y=nan */
- dllthomas 12y agoI dislike the fact that NaN taints other numbers - for precisely the same reason. Much better, in terms of early failure, would be signalling NaN or something like... ... maybenan_double maybe_nan = 0.0 / 0.0; double x = 1.0; if(is_nan(maybe_nan)) { fprintf(stderr, "maybe_nan is NaN\n"); return 1; } double not_nan = from_maybenan(maybe_nan); double y = not_nan + x; ... Of course, there are other dimensions in which this is not better, but I don't like silent propagation if it might stretch over a lot of code.
- benjamincburns 12y agoThe comments on this point to another question of which this is an exact duplicate. Interested parties should definitely read the top answer there, as it was written by a member of the IEEE-754 committee. http://stackoverflow.com/a/1573715/203705 http://stackoverflow.com/a/1573715/203705
- stephencanon 12y agoHey, that’s my answer! /wave Strangely, I was just adding some more material to this answer as I’ve never really been completely satisfied with it; then I flip over to hacker news and there it is. Anyway, if anyone has other IEEE-754 questions, I’ll try to answer.
- haberman 12y agoWow, I never would have guessed that a significant reason was historical, so that x != x could provide a robust way to detect NaN without requiring a special API. Is there a similar test for infinity?
- jules 12y agoThat reasoning is incorrect. If they had chosen NaN == NaN, then the test for NaN could be simply x == NaN... The other reason mentioned is also unconvincing. Surely preserving the law x==x is more important than the law x==y <=> x-y==0. Then we have: > Regarding your comment "that doesn't mean that the correct answer is false", this is wrong. The predicate (y < x) asks whether y is less than x. If y is NaN, then it is not less than any floating-point value x, so the answer is necessarily false. Clearly this reasoning is incorrect for the simple reason that the same reasoning can lead to the opposite conclusion. If y is NaN then we can't say that y < x is definitely true but neither can we say that y < x is definitely false. Here is my argument for why NaN == NaN should be true. The == operator for floats should rarely be used. If you want to know whether two floats can be considered the same, then you should always use some other condition like |x-y| < epsilon, and NOT x == y. The reason is that floating point arithmetic is inexact, so even a slight roundoff error will make x == y false. So if its job is not to test whether two floats are the same numerically then what is the job for the == operator? Its job is to test whether x and y are precisely the same float. The x == y operator should not pretend to work on the abstraction that x and y are kinda-sorta real numbers, because such an operator is useless anyway because the criterion for that needs to be application specific. It should compare floats while admitting that we are comparing floats which can take on the value +infinity -infinity +0 -0 and NaN, and we are not comparing real numbers which do not have those values. That's why NaN == NaN should be true.
- marcosdumay 12y agoThere's the obvious warning that one shall not compare floating point numbers for equality... But then, it's indeed quite surprizing. It get worse on pure tanguages where the expressions (0 / 0) and (0 / 0) are equal before evaluation, but different afterwards. Also, it trashes hash tables. Yet, NANs being different is the sanest mathematical definition. Maybe people should have opted for the ugliest choice because of real world concerns (it wouldn't be the first time), but this time, they didn't.
- tybenz 12y agolog(-1) -> exception 1/0 -> exception Problem solved. WTF is NaN even a thing??
- klodolph 12y agoThrowing exceptions for everything isn't a catch-all solution for writing better software—take a look at the Ariane rocket failure, which was caused by bounded numeric types throwing exceptions aggressively. Some modern programming styles tend to avoid enthusiastic non-local returns anyway in favor of things like monadic composition, such as Maybe in Haskell (which is more like NaN than it is like an exception). The nice thing about NaN is that you can just do a calculation as normal, and check for NaN at the very end. This means you don't have to do an expensive test/branch after every arithmetic or other numeric operation. The hardware is much, much easier to design if you don't have to make it branch, and the code is much faster without the compiler inserting branches. People who work with floating point numbers every day care very deeply about performance. If you don't care as much about performance, why not write your code in a language that does throw exceptions? Python, for example. Those of us that do use numerics love NaN, love signed zeros, and can live with NaN ≠ NaN even though it's kind of dumb.
- dllthomas 12y agoMaybe is more like NaN than an exception, but the nice thing about it as opposed to NaN is that you can isolate the particular failure. Sure, I can check for NaN in my doubles, but my types can't make sure I've checked for it by point X. Running a huge program to solve a problem, and getting back NaN with no explanation, is horrendously obnoxious.
- klodolph 12y agoHm, I have the opposite experience. Being able to ignore invalid data every once in a while trumps having a hundred hours of computation cut short by an unexpected exception bringing the whole thing down.
- 12y ago
- deleted 12y ago[deleted]
- quasque 12y agoThat was a very interesting read. I always thought NaN != NaN was because it's a class of values (there are many binary representations that are NaN, as it just requires an all-ones exponent and a mantissa that isn't all-zeroes) but I guess I was considering it too simplistically.
- zmmmmm 12y agoThis might actually explain a very intermittent bug that I spent some hours on and never solved. A sort that would never terminate, and the best I could come up with was that for reasons I couldn't understand the comparison function was returning false when an item was compared to itself ... TIL.
- xenadu02 12y agoSome of the links I posted to stack overflow; the top-voted answer there (as of this writing) was wildly incorrect, claiming it to be a mistake. It is not. Any invalid calculation produces NaN as opposed to raising an exception/signal because that would be non-portable (especially in the early 80s). The goal was that a correct algorithm on one platform or in one language would translate and be correct somewhere else. Further, some algorithms depend on being able to probe a function to find its bounds, requiring them to be able to locate the out of bounds condition without halting execution; doing that in a safe, portable way is actually quite hard unless you do it in software as part of the spec, hence NaN. If NaN==NaN, then (NaN/NaN)==1, etc etc and you still end up in a world of nonsense as far as naive algebra is concerned. This is just some of my fellow programmers whining and crying about having to think about floating point, the same sort of complaining I hear about Unicode (and full of just as much self-sure awful advice that produces wildly incorrect results for everything except the complainer's specific use case). http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.ht... http://www.eecs.berkeley.edu/~wkahan/ieee754status/754story.html http://www.eecs.berkeley.edu/~wkahan/ieee754status/754story.... http://cnx.org/content/m32770/latest/ http://cnx.org/content/m32770/latest/
- lbarrett 12y agoIt's not just "having to think about floating-point"--it's that a number of other things break. Easy function memoization doesn't work with NaNs because NaN!=NaN, so the function arguments look different, so the saved memoized value is never used.
- personjerry 12y agoBefore you join this discussion -- read the third answer posted, it is the "most correct" one.
- InclinedPlane 12y agoNot a number just tells you what something isn't, not what it is. A NaN could be a string, or a potato, or an airplane, or the prime minister of Djibouti. Is a potato equal to an airplane?
- shalmanese 12y agoNaN == NaN should return NaB, Not a Boolean.
- shawnz 12y agoYes, and if you try and branch based on that value, the CPU will jump to "Not an Address" and execute the instruction "Not an Opcode" over and over.
- wglb 12y agoA very good post. Most here don't remember the totally sad state of floating point before this came to be.
- dsugarman 12y agoalso anyone who works in SQL should know why NULL != NULL and NULL = NULL come back as ? and never true
- letney 12y agoAs a C/C++ and graphics programmer, I love NaN. It allows saving space by not having to carry an extra is_valid boolean for uninitialized values or regions of data that are masked. For textures, they are great as they don't interpolate w/ nearby neighbors and properly discard fragments & vertices without the aforementioned cost of an extra boolean attribute.
- jamesbrownuhh 12y agoOn the subject of Not A Number, is it a good time to recall Gary Bernhardt's excellent "Wat" piece? http://www.youtube.com/watch?v=HAw2jU4de2A http://www.youtube.com/watch?v=HAw2jU4de2A