4

我正在尝试通过将浮点数转换和转换为其他类型来运行一些测试,并且我想将我的浮点变量设置为不同的 nan 值。

“IEEE 浮点标准单精度(32 位)NaN 的逐位示例将是:s111 1111 1axx xxxx xxxx xxxx xxxx xxxx 其中 s 是符号(在应用程序中最常被忽略),a 确定类型NaN,并且 x 是一个额外的有效载荷(在应用程序中通常被忽略)。如果 a = 1,它是一个安静的 NaN;如果 a 为零且有效载荷非零,那么它是一个信令 NaN"

基本上我想找到一种方法来设置表示的有效负载或xxxx。有没有办法在c中做到这一点?

4

3 回答 3

8

您可以通过将适当的字符串传递给 C99 nan, nanf,nanl函数来控制“有效负载”位,但这些只会生成安静的 NaN,并且未指定字符串的解释(大多数实现将其视为十六进制数)。

或者,使用联合:

#ifndef __STDC_IEC_559__
#error "This program requires IEEE floating point arithmetic"
#endif

#include <stdint.h>
#include <assert.h>

static_assert(sizeof(float) == sizeof(uint32_t),
    "This program requires float to be 32 bits exactly");

float nanf_with_payload_bits(uint32_t payload)
{
   if (payload & 0x7FA00000) abort();

   union ieee_single {
       float f;
       uint32_t i;
   } nan;

   nan.i = 0x7FA00000 | payload;
   return nan.f;
}

当两种类型的大小完全相同时,写入联合的一个成员然后从另一个成员读取,不会在 C99+勘误表中引发未定义的行为。(这在 C89 中未定义的行为,但大多数编译器将其定义为执行您期望的操作。在 C++ 中它可能仍然是未定义的行为,我不确定;但是,同样,大多数编译器将其定义为执行您期望的操作。 )

如果您使用此函数创建信令NaN,请注意它们的行为在 C99/C11 附录 F 中明确未定义。

不要试图将i联合的组件分解为具有位域的结构。结构中位域的内存布局部分是实现定义的,部分是未指定的,特别是位域序列不一定按照与 CPU 字节序相同的顺序打包到一个字中(或者,实际上,正确打包完全)。


标准引用(所有 C99):

  • 这种联合的使用只是未指定的行为:6.2.6.1p7;J.1
  • 结构中位域的布局是不可预测的:6.2.6.1p1,2;6.7.2.1p10、11、13;J.3.9
  • 信号 NaN 的行为未定义:F.2.1
于 2012-04-28T16:27:12.257 回答
4

使用memcpy

int32_t i = 0x7FC00000;
float f;
memcpy(&f, &i, sizeof(f));

您也可以断言sizeof(f) == sizeof(i),但如果您知道浮点数是 IEEE,那么大概您也知道基本类型的大小。

于 2012-04-28T16:53:13.077 回答
3

有一种支持的方法可以在 C 中编写安静 NaN 的有效负载。

nan、nanf 和 nanl 函数(位于 math.h 头文件中,1999 C 规范的第 7.12.11.2 节)接受字符串作为参数。strtof、strtod 和 strtold 函数(stdlib.h 头文件,第 7.20.1.3 节)接受“NAN(字符序列)”形式的字符串。fscanf 和 sscanf 函数遵循 strtod。但是,字符序列以实现定义的方式进行解释。(这意味着您的编译器应该为您提供指定解释的文档。一些编译器将不遵守标准的这一要求。)

fprintf 函数(stdio.h,第 7.19.6.1 节)可以输出具有浮点格式(a、e、f、g)的“NAN(字符序列)”形式的字符串,printf 和 sprintf 也是如此。该标准允许输出没有字符序列的“NAN”,因此这不适用于许多编译器。

因为解释是实现定义的,所以你不应该期望它们是可移植的。它们通常用于特殊目的,例如调试。

于 2012-05-01T16:06:50.340 回答