Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Positive vs negative nans

I have some numerical code that was developed on AMD64 Linux (using LLVM 3.2).

I have recently ported it to OSX 10.9 with XCode. It runs fine, but it fails a lot of the unit tests: it seems that some calculations which on Linux return NaN (or -NaN) now return, on OSX, -NaN (or NaN).

Can I safely assume that positive and negative NaNs are equivalent and adjust my unit tests to accept either as a success, or is this a sign of something more serious going wrong?

like image 554
David Given Avatar asked Jan 25 '14 11:01

David Given


2 Answers

There is no notion of a "negative NaN" in IEEE-754 arithmetic. The NaN encoding still has a sign bit, and there is a notion of a "sign bit" operation which uses or affects this bit (copysign, abs, a few others), but it does not have any meaning when the NaN encoding is interpreted as a value. Many print routines happen to print the bit as a negative sign, but it is formally meaningless, and therefore there isn't much in the standard to govern what its value should be (except w.r.t. the aforementioned functions).

Here's the relevant section of IEEE-754 (2008):

Conversion of a quiet NaN in a supported format to an external character sequence shall produce a language-defined one of “nan” or a sequence that is equivalent except for case (e.g., “NaN”), with an optional preceding sign. (This standard does not interpret the sign of a NaN.)

So your platform's conversion functions may print the "sign" of NaN values, but it has no meaning, and you shouldn't consider it for the purposes of testing.

Edited to be a bit stronger: it is almost always a bug to attach meaning to the "sign bit" of a NaN datum.

like image 75
Stephen Canon Avatar answered Sep 30 '22 15:09

Stephen Canon


It depends entirely on what your unit tests are testing.

Most likely you'll be able to treat them as equivalent unless the testing you're doing is actually of the IEEE754 floating point software itself, or the C runtime code that prints them. Otherwise, you should treat them as identical if the code that uses what you're testing treats them as identical.

That's because the tests should echo your real usage, in every circumstance. An (admittedly contrived) example is if you're testing the function doCalc() which returns a double. If it's only ever used thus:

x = doCalc()
if x is any sort of Nan:
    doSomethingWithNan()

then your test should treat all NaN values as equivalent. However, if you use it thus:

x = doCalc()
if x is +Nan:
    doSomethingForPositive()
else:
    if x is -Nan:
        doSomethingForNegative()

then you'll want to treat them as distinct.

Similarly, if your implementation creates a useful payload in the fractional bits (see below), and your real code uses that, it should be checked by the unit tests as well.


Since a NaN is simply all 1-bits in the exponent and something other than all zero bits in the fraction, the sign bit may be positive or negative, and the fractional bits may be a wide variety of values. However, it's still a value or result that was outside the representation of the data type so, if you were expecting just that, it probably makes little difference what the sign or payload contain.

In terms of checking the textual output of NaN values, the Wikipedia page on NaN indicates that different implementations may give you widely varying outputs, among them:

nan
NaN
NaN%
NAN
NaNQ
NaNS
qNaN
sNaN
1.#SNAN
1.#QNAN
-1.#IND

and even variants showing the varying sign and payload that have no affect on its NaN-ness:

-NaN
NaN12345
-sNaN12300
-NaN(s1234)

So, if you want to be massively portable in your unit test, you'll notice that all the output representations bar one have some variant of the string nan in them. So a case-insensitive search through the value for the string nan or ind would pick them all up. That may not work in all environments but it has a very large coverage.

For what it's worth, the C standard has this to say about outputting floating point values with %f (%F uses uppercase letters):

A double argument representing a NaN is converted in one of the styles [-]nan or [-]nan(n-char-sequence) - which style, and the meaning of any n-char-sequence, is implementation-defined.

So it would suffice there to simply check if the value had nan somewhere inside it.

like image 27
paxdiablo Avatar answered Sep 30 '22 16:09

paxdiablo