Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert signalling NaN to quiet NaN?

I want to convert signalling NaN to quiet NaN in C. Could anybody suggest a method?

Thanks.

like image 769
kp11 Avatar asked Oct 17 '25 13:10

kp11


1 Answers

I guess I'll expand on my comment and provide a solution.

The tricky part here is being able to read/compare the sNaN without triggering an exception. After all it's called "signalling" for a reason. Wikipedia says that even comparison operations on sNaN will trigger an exception.

So a direct use of number != number or isnan(value) probably don't work because they invoke comparisons and will trigger a hardware exception. (I'm not entirely sure how isnan(value) is implemented though.)

EDIT : Correction, it looks like isnan() will never trigger an exception even on a signalling NaN so that makes the rest of this answer pointless.

The predicate isNaN(x) determines if a value is a NaN and never signals an exception, even if x is a signaling NaN.

Meaning it can be done as just this as suggested by Chrisoph in the comments:

if(isnan(value))
    value = NAN;

Here's my original answer that doesn't use isnan(value):

So the only way I can think of doing this is to go the bitwise route.

Assuming float is standard IEEE single-precision and int is a 32-bit integer, then here's one way to go about this: (Note that I haven't tested this.)

union{
    int i;
    float f;
} val;

val.f = //  Read the value here.

//  If NaN, force it to a quiet NaN.
if ((val.i & 0x7f800000) == 0x7f800000){
    val.i |= 0x00400000;
}

Note that this approach is not completely C-compliant and will invoke implementation defined behavior. Also note that this approach is not particularly efficient due to the need to move data between the FP and integer units.

Here's how this works:

  1. The union obviously is used to get the bits of the float into an int.
  2. All NaNs will have the bits in the 0x7f80000 set. The if-statement test will check if all of these bits are set.
  3. i |= 0x00400000; forces the NaN to a quiet NaN. Bit 22 determines whether the NaN is silent or quiet. Forcing it to 1 will make it a quiet NaN.

EDIT 2: If you can't use unions, here's are some other approaches (each of which have their own drawbacks):

Method 1:

float f = //  Read the value here.

int i = *(int*)&f;
if ((i & 0x7f800000) == 0x7f800000){
    i |= 0x00400000;
}

f = *(float*)&i;

Downside: It violates strict aliasing, but will probably still work.

Method 2:

char buf[sizeof(float)];

float f = //  Read the value here.

*(float*)&buf = f;
int i = *(int*)&buf;

if ((i & 0x7f800000) == 0x7f800000){
    i |= 0x00400000;
}

*(int*)&buf = i;
f = *(float*)&buf;

Same idea works with memcpy().

Downside: If alignment matters, you need to make sure buf is aligned.

Method 3: Implement your own isnan():

See this question: Where is the source code for isnan?

like image 78
Mysticial Avatar answered Oct 19 '25 04:10

Mysticial