Various equalities of NaN?

I know of three functions for checking for equality: ==, ===, and isequal. The documentation comments on subtlety regarding floating point numbers, and there is indeed a difference:

julia> NaN == NaN
false

julia> isequal(NaN, NaN)
true

julia> NaN === NaN
true

Could you explain why they behave this way? I am particularly interested in

  1. Why isequal was designed to behave differently from == for float numbers.
  2. Why NaN === NaN is true. For most other types, I would have naively assumed a === b implies a==b since x===y
 Determine whether x and y are identical, in the sense that no program could distinguish them.

according to the documentation.

In terms of my knowledge level, I know floating point numbers are represented as bitstrings with a sign bit, exponent bits, and mantissa bits, but don’t really know much else.

Thanks in advance!

How NaN are handled is outlined in IEEE-754. All comparisons between NaN and any number including itself return false.

=== on the other hand is a different operation that checks that no operation could tell them apart. For mutable structs this is pointer equality and for immutable structs this is bitwise equality.

There are actually several different representations for NaN’s, which are not ===. The best way to check if a value is NaN is with the function isnan

5 Likes

Thanks for pointing me to IEEE-754. I’ve always thought of == as checking for equality of value, and did not know there was a specification given by IEEE. Now it makes sense that NaN==NaN is false but Nan === Nan.

Do you also happen to know why isequal was designed to return true for two NaN in Julia?

NaNs not being equal to each other can often be unhelpful behavior. For those cases isequal is simpler then having to explicitly handle NaN’s.

=== isn’t much help for those cases as there can be multiple different bitwise representations of NaNs.

1 Like

As @WschW notes, there is only one best practice when it comes to determining if one or more values is “not a number” (NaN): use isnan(x) or, to determine if two values are NaNs, use e.g. nans(x,y) = isnan(x) && isnan(y) . And, fortunately, isnan(x) runs fast.

2 Likes

isequal is used in hashing, so it has to work like this. See

for a related discussion.

1 Like

For floats isequal is defined differently then ===. you can see the definition for how the former handles floats is:

isequal(x::AbstractFloat, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y)

This function returns true if both values are NaN’s.

This is distinct behaviour from === which in this case would return false if either the sign or any part of the mantissa differed.

1 Like

Ah that makes sense. Did not know isequal was the function used for hashing. Thanks!

Thank you all for clarifying the differences as well as why they exist!