How to convert signalling NaN to quiet NaN?

落爺英雄遲暮 提交于 2019-12-13 12:43:13

问题


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

Thanks.


回答1:


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?




回答2:


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.



来源:https://stackoverflow.com/questions/8180752/how-to-convert-signalling-nan-to-quiet-nan

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!