# Various equalities of NaN?

**URL:** https://discourse.julialang.org/t/various-equalities-of-nan/42649
**Category:** New to Julia
**Tags:** question
**Created:** [July 7, 2020, 1:58am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649 "2020-07-07T01:58:32Z")
**Posts on this page:** 14
**Page:** 1

<div class="post-metadata">

### Author: ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)
#### Post date: [July 7, 2020, 1:58am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/1 "2020-07-07T01:58:32Z")

</div>

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
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`

```julia
 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!

---

<div class="post-metadata">

### Author: ![WschW](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wschw/32/6575_2.png) [@WschW](https://discourse.julialang.org/u/WschW)
#### Post date: [July 7, 2020, 2:19am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/2 "2020-07-07T02:19:39Z")

</div>

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`

---

<div class="post-metadata">

### Author: ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)
#### Post date: [July 7, 2020, 3:45am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/3 "2020-07-07T03:45:25Z")

</div>

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?

---

<div class="post-metadata">

### Author: ![WschW](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wschw/32/6575_2.png) [@WschW](https://discourse.julialang.org/u/WschW)
#### Post date: [July 7, 2020, 4:10am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/4 "2020-07-07T04:10:44Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)
#### Post date: [July 7, 2020, 4:38am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/5 "2020-07-07T04:38:59Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [July 7, 2020, 5:35am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/6 "2020-07-07T05:35:31Z")

</div>

> [@tomohiro\_soejima](#):
>
> why `isequal` was designed to return `true` for two `NaN` in Julia?

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

> <https://github.com/JuliaLang/julia/issues/27545>
>
> One plausible and tempting upshot of the conversation at https://discourse.julia…lang.org/t/possible-bug-in-unique-set/5371 is that it might make sense for Julia to sort and hash -0.0 and 0.0 the same. It's a long discussion covering many topics, but it seems that other languages (numerical and otherwise) tend to consider these two values not to be distinct for sorting, uniquing and hashing purposes. This unfortunately does not remove the need for separating \`==\` and \`isequal\` but it does reduce the difference somewhat. I don't think this would break much real-world code.

for a related discussion.

---

<div class="post-metadata">

### Author: ![WschW](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wschw/32/6575_2.png) [@WschW](https://discourse.julialang.org/u/WschW)
#### Post date: [July 7, 2020, 5:47am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/7 "2020-07-07T05:47:57Z")

</div>

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

```julia
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.

---

<div class="post-metadata">

### Author: ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)
#### Post date: [July 7, 2020, 7:22am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/8 "2020-07-07T07:22:01Z")

</div>

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

---

<div class="post-metadata">

### Author: ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)
#### Post date: [July 7, 2020, 7:22am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/9 "2020-07-07T07:22:40Z")

</div>

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

---

<div class="post-metadata">

### Author: ![hyrodium](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hyrodium/32/27015_2.png) [@hyrodium](https://discourse.julialang.org/u/hyrodium)
#### Post date: [January 11, 2023, 11:23am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/10 "2023-01-11T11:23:58Z")

</div>

Additional info: sometimes `NaN === NaN` seems not satisfied.

```julia
julia> NaN, Inf-Inf, 0/0
(NaN, NaN, NaN)

julia> NaN === Inf-Inf
false

julia> NaN === 0/0
false

julia> Inf-Inf === 0/0
true

julia> versioninfo()
Julia Version 1.8.4
Commit 00177ebc4fc (2022-12-23 21:32 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 16 × AMD Ryzen 7 2700X Eight-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, znver1)
  Threads: 1 on 16 virtual cores

```

This is because `NaN` may have different internal expressions.

```julia
julia> bitstring(NaN)
"0111111111111000000000000000000000000000000000000000000000000000"

julia> bitstring(Inf-Inf)
"1111111111111000000000000000000000000000000000000000000000000000"

```

See [IEEE 754 - Wikipedia](https://en.wikipedia.org/wiki/IEEE_754#NaNs) for more information.

> The representation of NaNs specified by the standard has some unspecified bits that could be used to encode the type or source of error; but there is no standard for that encoding.

---

<div class="post-metadata">

### Author: ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)
#### Post date: [January 11, 2023, 11:45am UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/11 "2023-01-11T11:45:47Z")

</div>

I think part of the confusion arises from the `show` method printing `-NaN` as `NaN`:

```julia
julia> bitstring(NaN)
"0111111111111000000000000000000000000000000000000000000000000000"

julia> bitstring(-NaN)
"1111111111111000000000000000000000000000000000000000000000000000"

julia> -NaN
NaN

julia> NaN ≡ -NaN
false

julia> NaN ≡ -(Inf-Inf) ≡ -(Inf*0)
true

julia> -NaN ≡ Inf-Inf ≡ Inf*0
true

```

---

<div class="post-metadata">

### Author: ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)
#### Post date: [January 11, 2023, 4:07pm UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/12 "2023-01-11T16:07:33Z")

</div>

It’s not just about the sign of NaN.

A IEEE754 double precision (`Float64`) value has 1 sign bit, 11 exponent bits, and 52 significand bits. There are a few major categories of numbers:

- If the sign bit is 1 the value is negative-signed, if 0 then positive-signed
- If the exponent bits are all 1 AND the signficand bits are all 0, the value is `±Inf`
- If the exponent bits are all 1 AND any significand bit is 1, the value is `NaN`
- If the exponent bits AND significand bits are all 0, the number is `±0.0`
- If the exponent bits are 0 AND any signficand bit is 1, the value is “subnormal”
- Else (if the exponent is not all 0 or all 1) the number is in the normal range

See [wikipedia](https://en.wikipedia.org/wiki/IEEE_754) for more info.

So it’s not just that we don’t print the sign of `NaN`. There is the 52 bits of payload that we also don’t print. The reason we don’t print them is that they really don’t matter (for mathematical purposes). Arithmetic operation on a `NaN` will return the “same” result (modulo the sign and payload of a `NaN`, assuming the result is `NaN`) regardless of what `NaN` is passed in. There are some non-arithmetic functions (such as `signbit(NaN)`, `copysign(1.0,NaN)`) where the sign of the `NaN` may affect the result.

There are 2^53-2 distinct representations of 64-bit `NaN` (2 different signs, 2^52-1 different significands since one is used by `Inf`). Each will only be `===` with exactly itself (`===` requires the bit patterns to be identical). Note, also due to IEEE754 semantics, that `-0.0 == 0.0` (but still `-0.0 !== 0.0`). As a bit of trivia, `==` and `===` are equivalent for IEEE floats so long as at least one argument is neither `-0.0`, `+0.0`, or a `NaN`.

Note that I don’t think Julia (or your hardware, which Julia defers to in most cases) usually produces 64-bit `NaN`s other than `0x7ff8000000000000` and `0xfff8000000000000` by “natural” means. But you certainly can introduce them via things like `reinterpret(Float64,-1)`.

IEEE754 has nothing to say about `===`. It’s a different construct for an entirely different purpose than anything IEEE754 cares about. Without `===`, `NaN`s are pretty much interchangeable modulo the effect of the sign bit on some operations.

---

<div class="post-metadata">

### Author: ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)
#### Post date: [January 11, 2023, 5:36pm UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/13 "2023-01-11T17:36:36Z")

</div>

> [@mikmoore](#):
>
> Note that I don’t think Julia (or your hardware, which Julia defers to in most cases) usually produces 64-bit `NaN`s other than `0x7ff8000000000000` and `0xfff8000000000000` by “natural” means.

You also have (so rather use isequal and !isequal):

```julia
julia> NaN32 !== NaN # There's also NaN16 for Float16, and there are different such e.g. bfloat, and conceivably NaN8, Float8 is available in a package I believe, also Posits have NaR.
true

while:

julia> NaN !== NaN
false

```

---

<div class="post-metadata">

### Author: ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)
#### Post date: [January 11, 2023, 5:48pm UTC](https://discourse.julialang.org/t/various-equalities-of-nan/42649/14 "2023-01-11T17:48:13Z")

</div>

`x === y` can only be true if `typeof(x) == typeof(y)`. So `NaN32 !== NaN64` just like `1.0f0 !== 1.0e0`.
