将位设置为double并使用g ++的优化标志进行编译

时间:2011-06-22 12:38:03

标签: c++ bit-manipulation

我正在尝试将位设置为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.

所以我想知道这是否是优化器的错误,或者我在这里做错了什么。

3 个答案:

答案 0 :(得分:3)

是的,您违反了该语言的别名规则。不允许通过指向另一种类型的指针写入一种类型的对象(char*除外)。

由于您从未写过代码中的任何double,因此允许编译器假定永远不会为t赋值。 (并输出错误本身: - )

GCC有一个扩展,允许您编写一种类型的值,并将其作为另一种类型读取,如果您将它们都放在一个联合中。这虽然是编译器特定的(但是半便携式,因为其他人必须遵循领先)。

答案 1 :(得分:2)

从技术上讲,你有未定义的行为,虽然它显然是 标准的意图,这在明显的情况下工作,它的 如果它可以看到,编译器的反常打破它 reinterpret_cast。如果您知道平台的字节顺序,那么 可以通过使用uint8_t(字符类型)来解决问题 操纵位,或memcpyuint64_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;
}