我正在使用带有效负载的NaN(因此尾数包含重要信息,但仍被视为NaN)。例如,一个这样的值可以用十六进制的IEEE-754-1985表示为FFF8000055550001
,其符号位为1,NaN /无穷大的7FF
的指数,安静的NaN位集(至少在大多数架构上),以及0x55550001
的有效载荷。
然而,这有一些问题。首先,这不能像C / C ++中的文字一样容易地创建,因为常用方法都不能用于初始化文字:
inf
和p1024
获得p-1024
s,但在这种情况下忽略尾数)memcpy
很好,因为它避免了别名但需要函数调用reinterpret_cast
只是C ++(可以)但是要求操作数是变量或指针而不是文字是否有任何方法为带有效负载的NaN设置静态常量?可以假设该系统符合IEEE-754-1985并且long
s和double
s具有相同的字节序。
您可以使用复合文字来执行此操作,然后您可以获取和转换的地址:
double d = ((union {unsigned char c[8]; double d; }){ .c={1,0,0,0,0,0,0xf0,0x7f} }).d;
printf("d=%f\n", d);
int i;
printf("d=0x");
for (i=0; i<sizeof(double); i++) {
unsigned char c = ((unsigned char *)&d)[sizeof(double)-1-i];
printf("%02x", c);
}
printf("\n");
在这里,我们有一个匿名文字联合,包含一个大小为8的unsigned char
数组和一个double
。我们初始化文字的数组字段并读取double
部分以初始化变量。
输出:
d=nan
d=7ff0000000000001
我们可以用宏来清理它,并且还要处理字节序:
static_assert(sizeof(double)==8, "unexpected double size");
#if __BYTE_ORDER == __BIG_ENDIAN
# define DOUBLE_LIT(c1,c2,c3,c4,c5,c6,c7,c8) ((union {unsigned char c[8]; double d; }){ .c={c1,c2,c3,c4,c5,c6,c7,c8} }).d
#elif __BYTE_ORDER == __LITTLE_ENDIAN
# define DOUBLE_LIT(c1,c2,c3,c4,c5,c6,c7,c8) ((union {unsigned char c[8]; double d; }){ .c={c8,c7,c6,c5,c4,c3,c2,c1} }).d
#else
# error unknown endianness
#fi
然后我们可以像这样使用它:
double d = DOUBLE_LIT(0x7f,0xf0,0,0,0,0,0,1);
请注意,字节顺序检查取决于系统。以上是它通常在Linux上实现的方式。
memcpy
不需要通过函数调用来实现。一个好的编译器会内联它并以其他方式优化它。一种可能性是GCC(非便携式)__builtin_nan
扩展,它可用于产生有效载荷的编译时NaN常量。
参考其文件:
如果给定一个字符串文字,所有这些函数都将由strtol使用,则该函数会被足够早地评估,以便将其视为编译时常量。
例:
#include <stdio.h>
#define MAKE_QNAN_WITH_PAYLOAD(sign, payload) \
sign __builtin_nan(#payload)
double d = MAKE_QNAN_WITH_PAYLOAD(-, 0x55550001);
int main(void)
{
// assume little-endian byte ordering
for (int i = sizeof(double)-1; i >= 0; i--)
{
printf("%.2x", ((unsigned char *)&d)[i]);
}
putchar('\n');
return 0;
}
结果:
fff8000055550001