4

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

Thanks.

Mysticial
  • 464,885
  • 45
  • 335
  • 332
kp11
  • 2,055
  • 6
  • 22
  • 25
  • Can you give some example ? I cannot understand what you meant to say ? – Neel Basu Nov 18 '11 at 10:19
  • Reading up this [question](http://stackoverflow.com/questions/2247447/usefulness-of-signaling-nan) makes me believe that you can do that by initializing a variable with the signaling NaN. – Bort Nov 18 '11 at 10:19
  • 1
    is this what you're looking for: `if(isnan(value)) value = NAN;` – Christoph Nov 18 '11 at 10:21
  • 1
    I think the tricky part here is being able to read the sNaN without triggering an exception. After all it's called "signalling" for a reason. Even comparisons with sNaN with cause an exception (http://en.wikipedia.org/wiki/Floating_point#NaNs). – Mysticial Nov 18 '11 at 10:37
  • @Mysticial : Consider I have already got the value in a float variable. If so, 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, but works for quite NaN. Hence I want to convert to quite NaN and then check for NaN. In case you have any other method, pls let me know. – kp11 Nov 18 '11 at 10:43
  • @kartik: that's exactly what `isnan()` is for - no need for `number != number` – Christoph Nov 18 '11 at 11:05
  • @Christoph: I think the issue here is that `isnan()` will probably trigger an exception on a signaling NaN. – Mysticial Nov 18 '11 at 11:07
  • 1
    @Mysticial: `isnan()` shouldn't raise a fp exception; I didn't find anything about that in the C standard, but afaik it's explicitly mentioned for the `isNaN()` predicate of IEEE 754, which most implementations at least try to follow... – Christoph Nov 18 '11 at 11:13
  • @Christoph: You're right, I just found the docs for it. I'll mention that in my answer. – Mysticial Nov 18 '11 at 11:15
  • Is there a reason you care about signaling NaNs? They'll never be generated unless you generate them yourself intentionally, and many (most?) platforms don't even have any special signaling NaN treatment as far as I know... – R.. GitHub STOP HELPING ICE Nov 18 '11 at 14:49

2 Answers2

5

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?

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • Thanks! unfortunately I am not allowed to use unions :( and there is no isnan() in my environment. – kp11 Nov 18 '11 at 12:06
  • Is there a way to mask that 22nd bit without union? If yes then that is what I am looking for. The usual typecasting/masking will still result in crash for signalling NaN. – kp11 Nov 18 '11 at 12:30
  • @kartik: `memcpy()`ing the value to an `int` variable *might* work, especially if the fp variable is `volatile` (which, however, would carry a performance penalty); it seems you'll probably need to drop to asm level... – Christoph Nov 18 '11 at 13:10
  • You could also use pointer-casts. To get around strict-aliasing you can do it through a char buffer first since `char*` is exempt from the strict-aliasing rule. If you want maximum efficiency, you can do everything using SSE intrinsics which will easily let you get around this union problem as well as vectorizing in the process. (Though I'm curious as to why you don't have `isnan()` and why you can't use unions.) – Mysticial Nov 18 '11 at 13:27
  • You probably should change 0x7f80000 to 0x7f800000 everywhere – mambo_sun Nov 15 '17 at 17:48
  • @mambo_sun Thanks, not sure how that slipped through for so many years. – Mysticial Nov 18 '17 at 09:59
  • The `isnan(value)` method might not work because I've read that just loading a signalling NaN into a float register might trigger the FP exception. – Emile Cormier May 16 '21 at 16:48
  • https://stackoverflow.com/questions/2247447/usefulness-of-signaling-nan – Emile Cormier May 16 '21 at 17:39
1

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.

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • I do not have isnan() in my environment. – kp11 Nov 18 '11 at 12:05
  • @kartik: what is your environment? in case of MSVC, use `_isnan()` (see http://msdn.microsoft.com/en-us/library/aa298428%28v=VS.60%29.aspx ) – Christoph Nov 18 '11 at 12:08
  • No.. mine is an embedded environment with minimum libraries. If there is a work around, I am the one who should implement that. – kp11 Nov 18 '11 at 12:29