我想我会扩展我的评论并提供解决方案。
这里的棘手部分是能够在不触发异常的情况下读取/比较sNaN。毕竟它被称为“信号”是有原因的。 Wikipedia says that even comparison operations on sNaN will trigger an exception.
所以直接使用number != number 或isnan(value) 可能不起作用,因为它们会调用比较并且会触发硬件异常。 (不过我不完全确定isnan(value) 是如何实现的。)
编辑:更正,看起来isnan() 将永远不会触发异常,即使是在信号NaN 上,所以这个答案的其余部分毫无意义。
谓词 isNaN(x) 确定一个值是否为 NaN 并且从不
发出异常信号,即使 x 是信号 NaN。
这意味着它可以按照 Chrisoph 在 cmets 中的建议进行:
if(isnan(value))
value = NAN;
这是我不使用isnan(value)的原始答案:
所以我能想到的唯一方法就是走位路线。
假设 float 是标准的 IEEE 单精度,int 是一个 32 位整数,那么这里有一种解决方法:(请注意,我没有对此进行测试。)
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;
}
请注意,这种方法并不完全符合 C 语言,并且会调用实现定义的行为。另请注意,由于需要在 FP 和整数单元之间移动数据,这种方法并不是特别有效。
这是如何工作的:
- 联合显然用于将
float 的位转换为int。
- 所有
NaNs 都将具有0x7f80000 集中的位。 if 语句测试将检查所有这些位是否都已设置。
-
i |= 0x00400000; 将NaN 强制为安静的NaN。第 22 位决定 NaN 是静默还是静默。将其强制为 1 将使其成为安静的 NaN。
编辑 2:如果您不能使用联合,这里有一些其他方法(每种方法都有自己的缺点):
方法一:
float f = // Read the value here.
int i = *(int*)&f;
if ((i & 0x7f800000) == 0x7f800000){
i |= 0x00400000;
}
f = *(float*)&i;
缺点:它违反了严格的别名,但可能仍然有效。
方法二:
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;
同样的想法适用于memcpy()。
缺点:如果对齐很重要,您需要确保buf 对齐。
方法3:实现自己的isnan():
看到这个问题:Where is the source code for isnan?