# Inconsistency of NaN

`0/0` returns `NaN`
`NaN*false` returns `0.0`
`(0/0)*false` returns `-0.0`
Isn’t this an inconsistency of NaN?
Also:
`julia> (NaN*(0/0))*false`
`0.0`
but:
`julia> ((0/0)*NaN)*false`
`-0.0`

1 Like

It appears that these are two different bit values although they both print as `NaN`

``````julia> reinterpret(UInt,0/0)
0xfff8000000000000

julia> reinterpret(UInt,NaN)
0x7ff8000000000000

julia> 0/0 == NaN
false
``````

they differ only by a single bit, presumably the sign bit

1 Like

Ok, but:
`julia> 0.0 == -0.0`
`true`

though:
`julia> 0.0 === -0.0`
`false`

so at least

``````julia> (0/0)*false == NaN*false
true
``````
1 Like

That’s correct, they can’t be egal (`===`) because the sign bit is different:

``````julia> reinterpret(UInt, 0.0)
0x0000000000000000

julia> reinterpret(UInt, -0.0)
0x8000000000000000
``````

or

``````julia> bitstring(0.0)
"0000000000000000000000000000000000000000000000000000000000000000"

julia> bitstring(-0.0)
"1000000000000000000000000000000000000000000000000000000000000000"
``````
4 Likes

`julia> (NaN*(0/0))*false`
`0.0`
but:
`julia> ((0/0)*NaN)*false`
`-0.0`

In IEEE-754, the IEEE standard for floating point arithmetic, which Julia and most other languages use, `NaN`s are encoded with the exponent field filled with ones. According to the standard, the sign bit does not matter for determining `NaN`ness, and the sign bit is precisely what’s different between `0/0` and `NaN`. I’m not sure exactly why `0/0` results in a different sign bit, but it could well just be an implementation detail. Next, there’s the multiplication with the `Bool` `false`, which (eventually) hits this method:

The `copysign` is what causes the sign bit of the `NaN` to appear in the final result. I’m not sure whether that’s intended. I suppose it is a little weird that information that’s supposed to be (mostly) immaterial has an observable effect here. Then again, it seems harmless.

6 Likes

Why is why the way to determine `NaN`ness is to use `isnan` and not any number of consecutive `=`'s. You’re not supposed to do `0/0 == NaN` to check if the lhs is `NaN`.

3 Likes

or use `haskey`

``````julia> haskey(Dict(NaN => nothing), 0/0)
true
``````

There are several things to unpack in this post.

1. `false*NaN` is special cased to make `false` “strong”, see the implementation

2. but the `copysign` method for `NaN`s is a bit misleading, as `NaN`s are not supposed to have a sign:

``````julia> sign(0/0)
NaN

julia> copysign(1.0, 0/0)
-1.0
``````

I don’t know the arcane details of IEEE 754 about whether this is the intended behavior. In any case, it is mostly innocuous.

3. `(NaN*(0/0))*false` is the same phenomenon, except the sign bit comes from the first operand.

6 Likes

It’s what the hardware does, iirc.

3 Likes

Or rather `isequal` (which is what `haskey` uses): `isequal(NaN, 0/0) == true`.

3 Likes

Whoa there. I don’t know if these are officially bugs but they are at odds with the IEEE 754 spec, which reads in part:

For an operation with quiet NaN inputs, other than maximum and minimum operations, if a floating-point result is to be delivered the result shall be a quiet NaN which should be one of the input NaNs.

I understand why people usually want false*x to be falsy but NaN is falsy enough and more correct than 0.0 for false*NaN.

2 Likes

I agree. But since this has been in the language for a while, I think a lot of programmers (myself included) use `false*x` on uninitialized data and expect to get rid of `NaN`s that way.

I think it would be nice if Julia had a “strong zero” in a type of its own. I even wrote a package implementing that a while back (though I never registered it or upgraded it to be 1.0 compatible.)

3 Likes

I think the argument is that IEEE-754 does not pertain to operations involving boolean inputs. I’m not picking sides on whether or not the current behavior of various operations with mixed floating point number and boolean inputs is ‘right’, but it’s not a clear-cut case.

1 Like