我正在尝试将位设置为double(IEEE标准754)。说我想'构建'一个3,我会设置双浮点表示的第51位和第62位,这样我得到二进制1.1 * 2,十进制是3.我写了这个简单的主:
int main() {
double t;
uint64_t *i = reinterpret_cast<uint64_t*>(&t);
uint64_t one = 1;
*i = ((one << 51) | (one << 62));
std::cout << sizeof(uint64_t) << " " << sizeof(uint64_t*) << " "
<< sizeof(double) << " " << sizeof(double*) << std::endl;
std::cout << t << std::endl;
return 0;
}
这个的输出是
8 8 8 8
3
使用g ++ 4.3进行编译时没有优化。但是,如果我添加-O2或-O3优化标志,我会得到一个奇怪的行为。也就是说,如果我只是按原样离开主,我得到相同的输出。但是如果我删除输出4 sizeof的行,那么我得到输出
0
没有sizeof输出的未优化版本也会正确返回3.
所以我想知道这是否是优化器的错误,或者我在这里做错了什么。
答案 0 :(得分:3)
是的,您违反了该语言的别名规则。不允许通过指向另一种类型的指针写入一种类型的对象(char*
除外)。
由于您从未写过代码中的任何double
,因此允许编译器假定永远不会为t
赋值。 (并输出错误本身: - )
GCC有一个扩展,允许您编写一种类型的值,并将其作为另一种类型读取,如果您将它们都放在一个联合中。这虽然是编译器特定的(但是半便携式,因为其他人必须遵循领先)。
答案 1 :(得分:2)
从技术上讲,你有未定义的行为,虽然它显然是
标准的意图,这在明显的情况下工作,它的
如果它可以看到,编译器的反常打破它
reinterpret_cast
。如果您知道平台的字节顺序,那么
可以通过使用uint8_t
(字符类型)来解决问题
操纵位,或memcpy
到uint64_t
,然后memcpy
结果回到double
。
union
,那么g ++将使这项工作成功。在所有条件下
访问通过联合类型。标准明确禁止这个,
然而(虽然它是标准前几天的首选解决方案),
我使用的编译器不起作用。
使用g ++,还有一个选项-fnostrict-aliasing
,它将生成
它有效。
答案 2 :(得分:1)
尝试:
int main()
{
volatile double t;
// ^^^^^^^^ Tells the compiler this may be modified unexpectedly.
volatile uint64_t& i = reinterpret_cast<volatile uint64_t&>(t);
uint64_t one = 1;
i = ((one << 51) | (one << 62));
std::cout << t << std::endl;
}