1

I'm working in an environment with a mostly absent double/Math library (NETMF). I wrote this class to make things easier:

public struct DoubleEx
{
    public const double NaN = 0.0d / 0.0d;
    public static bool IsNaN(double x)
    {
        return x != x;
    }
    ...
}

Seems like it should work, right?

Well, when I run this code:

Debug.Print("Method call: " + DoubleEx.IsNaN(DoubleEx.NaN));
Debug.Print("Method call: " + DoubleEx.NaN != DoubleEx.NaN);

I get this output:

False
True

Somehow, the act of putting it in a function breaks it! Is there some kind of optimization going on here? Or is the hardware misinterpreting the instructions?

Eric
  • 95,302
  • 53
  • 242
  • 374

3 Answers3

3

The following is based on the IEEE Standard 754:

// @struct IEEE_DOUBLEREP | allows bit access to 8 byte floats
//[StructLayout(LayoutKind.Sequential)]
//public struct ieee_doublerep
//{
//    ulong low_mantissa;       // @field low 16 bits of mantissa
//    ushort mid_mantissa;  // @field mid 16 bits of mantissa
//    uint high_mantissa:4;     // @field high 4 bits of mantissa
//    uint exponent:11;         // @field exponent of floating point number
//    uint sign:1;              // @field sign of floating point number
//};

public struct DoubleEx
{
    public const long NANMASK = 0x7FF0000000000000;
    public const long INFINITYMASK = 0x000FFFFFFFFFFFFF;

    public const double NaN = 0.0f / 0.0f;
    public const double NegativeInfinity = -1.0f / 0.0f;
    public const double PositiveInfinity = 1.0f / 0.0f;
    public static bool IsNaNBad(double x)
    {
        return x != x;
    }

    public unsafe static bool IsNaN(double value)        
    {
        long rep = *((long*)&value);
        return ((rep & NANMASK) == NANMASK &&
                ((rep & INFINITYMASK) != 0));
    }

    public unsafe static bool IsPositiveInfinity(double value)
    {
        double negInf = DoubleEx.PositiveInfinity;
        return *((long*)&value) == *((long*)&negInf);
    }

    public unsafe static bool IsNegativeInfinity(double value)
    {
        double posInf = DoubleEx.PositiveInfinity;
        return *((long*)&value) == *((long*)&posInf);
    }

    public unsafe static bool IsInfinite(double x)
    {
        long rep = *((long*)&x);
        return ((rep & NANMASK) == NANMASK &&
                ((rep & INFINITYMASK) == 0));
    }
}
Anthony Wieser
  • 4,351
  • 1
  • 23
  • 25
0

You have an operator precedence problem, and putting it inside a function changes the expression.

Try:

Debug.Print("Method call: " + (DoubleEx.NaN != DoubleEx.NaN));

And what about:

static bool DoubleInequal(double a, double b) { return a != b; }
static bool IsNaN(double x) { return DoubleInequal(x, x + 0.0); }
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • @Eric: What processor are you using? Does it have IEEE floating-point, or is .NETMF emulating floating-point operations in software, and may not even have logic for NaN at all? – Ben Voigt Jul 02 '11 at 13:59
  • A FEZ Panda II. I've asked WTF is going on on their forums [here](http://www.tinyclr.com/forum/2/3590/#/0/msg34053). I'm on a very tight deadline, and would appreciate knowing what's going on. – Eric Jul 02 '11 at 14:02
  • 2
    @Eric: Ok, found [the specs](http://www.ghielectronics.com/catalog/product/256) for that board, and [the processor](http://www.nxp.com/documents/data_sheet/LPC2387.pdf) turns out to be an ARM7TDMI. [This other question indicates that ARM7 has to emulate operations on doubles](http://stackoverflow.com/questions/3310907/what-are-the-advantages-of-armv7-over-armv6-when-compiling-iphone-apps), I suppose that NaN is not emulated properly. – Ben Voigt Jul 02 '11 at 14:07
  • Great. Does the same apply to floats? – Eric Jul 02 '11 at 14:10
  • @Eric: According to the other question, single-precision should be good. – Ben Voigt Jul 02 '11 at 14:11
  • Nope, singles suffer the same fate. Besides, the comparison in your answer works. It's when the NaN gets passed to a function that all fails. – Eric Jul 02 '11 at 14:17
  • @Eric: Hmmm, sounds like an invalid optimization. What if we add another function call (will edit my answer, check back in 60 seconds)? – Ben Voigt Jul 02 '11 at 14:20
  • Still no. Getting some wierd results when I `Debug.Print(a)`: I get `-nan`! NaN's aren't signed, are they? – Eric Jul 02 '11 at 14:39
  • @Eric: IEEE returns a whole bunch of values for NaNs. Since [the sign bit can vary between NaN values](http://en.wikipedia.org/wiki/NaN), I suppose you could say there are both positive and negative NaNs. – Ben Voigt Jul 02 '11 at 14:41
-1

Have you tried return x == NaN? It doesn't seem like good practice to me to assume x != x is synonymous with "IsNaN".

Tim S.
  • 55,448
  • 7
  • 96
  • 122
  • By definition, `NaN == x` returns false for all values of x. However, if I do add this to my code, I get the even more alarming result that `Double.IsNaN(0)` is true! – Eric Jul 02 '11 at 13:52
  • @Jason: Alarmingly, it does detect NaN. What it also does is thing 0 is NaN! – Eric Jul 02 '11 at 13:55