如何在C89代码中生成x87架构下的正NaN值?
在C89中通过计算生成整数表示为0x7fc00000的正IEEE 754单精度NaN
我需要用C89代码生成整数表示为0x7fc00000的正IEEE 754 32位float NaN值。
非C89标准的GCC实现方案
float mynan(void) { return __builtin_nanf(""); }
符合C89的union实现方案(假设sizeof(int) == 4或sizeof(long) == 4)
float mynan(void) { union { float f; unsigned u; unsigned long ul; } fu; if (sizeof(unsigned) == 4) { fu.u = 0x7fc00000U; } else { /* 假设sizeof(unsigned long) == 4,它总是>=4 */ fu.ul = 0x7fc00000UL; } return fu.f; }
测试代码
float mynan(void); typedef char assert_int_size[sizeof(int) == 4 ? 1 : -1]; int main(int argc, char **argv) { float f; union { float f; int i; } fi; (void)argc; (void)argv; fi.f = mynan(); printf("0x%x\n", fi.i); /* 输出0x7fc00000 */ }
核心问题
假设使用x87 FPU和非优化编译器(如gcc -O0),能否不使用union、<math.h>中的NAN(C89不支持)或memcpy/memmove等内存操作函数,仅通过C89代码触发计算,让FPU生成目标正NaN?
已尝试的方案及问题
- 生成负NaN的方案
float mynan(void) { float f = 1L << 30; f *= f; /* 2^60 */ f *= f; /* 2^120 */ f *= f; /* 2^240,超出32位float范围,变为INFINITY */ return f * 0.0f; /* 得到NaN,但结果是0xffc00000(负NaN) */ }
该方案生成的是负NaN,不符合需求。
- 尝试通过减法反转符号,但仍得到负NaN
float mynan(void) { float f = 1L << 30; f *= f; /* 2^60 */ f *= f; /* 2^120 */ f *= f; /* 2^240,变为INFINITY */ return 0.0f - f * 0.0f; }
结果依然是负NaN。
- 尝试取反,但兼容性差
float mynan(void) { float f = 1L << 30; f *= f; /* 2^60 */ f *= f; /* 2^120 */ f *= f; /* 2^240,变为INFINITY */ return -(f * 0.0f); }
该方案仅在部分编译器(如gcc -O0)中有效,其他编译器会将其优化等效于return 0.0f - (f * 0.0f),仍返回负NaN。
可行的计算方案
要生成正NaN,关键是让FPU生成正无穷大后再执行NaN生成操作,或者直接构造能产生正NaN的计算。
方案1:正无穷大相减
float mynan(void) { float f = 1.0f; // 不断乘2直到溢出为正无穷大 while (1) { float next = f * 2.0f; if (next == f) break; // 当f变为inf时,乘2后值不变 f = next; } // 正无穷大减正无穷大,生成符号位为0的NaN return f - f; }
在x87 FPU下,正无穷大相减会生成整数表示为0x7fc00000的正NaN。
方案2:正无穷乘正0
float mynan(void) { // 32位float的最大有限值约为3.4e38,乘2后直接溢出为正无穷 float pos_inf = 3.4e38f * 2.0f; // 正无穷乘正0,生成正NaN return pos_inf * 0.0f; }
该方案直接构造正无穷,再与正0相乘,得到符号位为0的目标NaN。
方案3:利用平方根运算(需依赖FPU设置)
#include <math.h> // C89标准支持sqrt函数 float mynan(void) { // 对负数开平方,FPU默认会生成NaN return sqrt(-1.0f); }
注意:该方案依赖x87 FPU的异常控制字设置,默认情况下会生成QNaN,多数实现中其整数表示为0x7fc00000,需测试确认。
内容的提问来源于stack exchange,提问作者pts
相关产品推荐
相关产品推荐

