Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why double.IsNaN has so strange implementation

So if decompile .net sources, you can find this code

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SecuritySafeCritical]
[__DynamicallyInvokable]
public static unsafe bool IsNaN(double d)
{
  return (ulong) (*(long*) &d & long.MaxValue) > 9218868437227405312UL;
}

so according to IEEE754 NaN != NaN

So question is simple: why it doesn't look like

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SecuritySafeCritical]
[__DynamicallyInvokable]
public static unsafe bool IsNaN(double d)
{
  return d != d;
}

My friend told me that implementation of == may be return !IsNan(this) && this.InnerEquals(other);

but afaik implementation of NaN is hard-coded on hardware layer, in processor itself. And we shouldn't process NaN case separately.


And one more question. Why it's so stupid?

bool b1 = (double.NaN == double.NaN); // false
bool b2 = double.NaN.Equals(double.NaN); //true

http://ideone.com/ZSRYz1

i know an implementation

[__DynamicallyInvokable]
public override bool Equals(object obj)
{
  if (!(obj is double))
    return false;
  double d = (double) obj;
  if (d == this)
    return true;
  if (double.IsNaN(d))
    return double.IsNaN(this);
  else
    return false;
}

but dunno WHY

like image 231
Alex Zhukovskiy Avatar asked Dec 11 '22 10:12

Alex Zhukovskiy


2 Answers

In IEEE754, a NaN value is encoded having a sign bit of 0 or 1 and an exponent section of all 1 bits. The value in the fraction section is irrelevant as long as they're not all 0 bits (if they are, it's one of the infinities rather than NaN).

Hence consider the 32-bit single precision float:

s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
x 11111111 ???????????????????????

I've used m (for mantissa) for the fraction bits above so as not to confuse it with the hex digit f.

Provided not all of those ? bits are zero, it's one of the NaN encodings. If you cast that to a 32-bit long, it's a simple matter to check against binary:

x 11111111 00000000000000000000000

and, if it's larger than the positive variant or less than the negative variant , that's one of the NaN encodings.

It's the ignoring of the sign that's being done by the & operation since the signed long.MaxValue will be 2^63 - 1 (assuming it's 64-bit) so that operation will simply force the most significant bit to 0.

The magic value is constructed from a 0 sign bit, an exponent of all 1 bits (for double precision, there are eleven of these, and all 0 bits in the fraction. Hence binary:

seee eeee eeee mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm
0111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

gives 0x7ff0000000000000 or 9218868437227405312.

like image 119
paxdiablo Avatar answered Dec 29 '22 22:12

paxdiablo


Hard to be sure without being the implementor, but my guess is that the designers considered signaling NaNs (SNAN). If exceptions are unmasked then testing for equality on an SNAN will raise a hardware floating point exception.

like image 23
David Heffernan Avatar answered Dec 29 '22 22:12

David Heffernan