kp11
kp11

Reputation: 2125

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.

Upvotes: 4

Views: 3283

Answers (2)

Mysticial
Mysticial

Reputation: 471229

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?

Upvotes: 5

Christoph
Christoph

Reputation: 169593

In the comments, you mention what you actually want to do:

before I perform any other operation, I would like to check for NaN i.e. number != number, but this will not work for signalling NaN

That's what the C99 isnan() macro is for, no need for number != number. The C standard itself doesn't really specify the semantics of signaling NaNs - all it says is

a signaling NaN generally raises a floating-point exception when occurring as an arithmetic operand

in section 5.2.4.2.2 §3 and

This specification does not define the behavior of signaling NaNs.

in annex F, section F.2.1 §1.

However, according to Wikipedia, IEEE 754 specifies an isNaN() predicate which does not raise an exception even when used with signaling NaN, and isnan() should behave accordingly on implementations which provide IEEE floating point semantics.

Upvotes: 1

Related Questions