我正在使用带有效负载的NaN(因此尾数包含重要信息,但仍被视为NaN)。例如,一个这样的值可以使用十六进制的IEEE-754-1985表示为FFF8000055550001
,其符号位为1,NaN /无穷大的指数为7FF
,安静的NaN位集(至少在大多数架构上),以及0x55550001
的有效载荷。
然而,这有一些问题。首先,这不能像C / C ++中的文字那样容易地创建,因为常用方法都不能用于初始化文字:
inf
和p1024
,但在这种情况下忽略尾数)p-1024
很好,因为它避免了别名但需要函数调用memcpy
仅限C ++(可以),但要求操作数是变量或指针而不是文字是否有任何方法可以为带有效负载的NaN设置静态常量?可以假设系统符合IEEE-754-1985并且reinterpret_cast
和long
具有相同的字节序。
答案 0 :(得分:2)
您可以使用复合文字执行此操作,然后您可以获取地址和演员:
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上实现的方式。
答案 1 :(得分:1)
memcpy
不需要通过函数调用来实现。一个好的编译器会内联并优化它。
可以初始化具有静态存储持续时间的常量联合对象。
答案 2 :(得分:1)
一种可能性是GCC(非可移植)__builtin_nan
扩展,可用于生成带有效负载的编译时NaN常量。
参考其文件:
这个函数,如果给出一个字符串文字,所有这些都是 由strtol消耗,被评估得足够早以至于被认为是a 编译时常量。
示例:
#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