Reputation: 2125
I want to convert signalling NaN to quiet NaN in C. Could anybody suggest a method?
Thanks.
Upvotes: 4
Views: 3283
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:
float
into an int
.NaNs
will have the bits in the 0x7f80000
set. The if-statement test will check if all of these bits are set.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
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