You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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?

已尝试的方案及问题

  1. 生成负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,不符合需求。

  1. 尝试通过减法反转符号,但仍得到负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。

  1. 尝试取反,但兼容性差
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 11:22:22