【问题标题】:Is it safe to transport a double through an int in C++03?在 C++03 中通过 int 传输 double 是否安全?
【发布时间】:2013-01-28 16:14:36
【问题描述】:

编辑:跳到我最新版本问题的水平线下方。

仅假设 sizeof(double) * CHAR_BITS

union u {
  std::int64_t m_int;
  double       m_double;
};

int main()
{
  double d = get_a_random_double();

  u u1;
  u1.m_double = d;

  u u2;
  u2.m_int = u1.m_int;

  assert(u2.m_double == d);
}

我担心标准可能无法保证 int64_t 值的副本会保留我们用来保存双精度的所有位。

另一方面,我很确定标准保证以下使用 chars 而不是 unt64_t 的替代方案始终满足断言:

struct chars {
  char m_x[sizeof(double)];
};

union u {
  chars  m_chars;
  double m_double;
};

int main()
{
  double d = get_a_random_double();

  u u1;
  u1.m_double = d;

  u u2;
  u2.m_chars = u1.m_chars;

  assert(u2.m_double == d);
}

你同意吗?


编辑:

好的,我必须承认标准不支持通过联合进行的转换。

那么我尝试在不使用联合的情况下通过一系列字符传输双精度数的情况如何:

int main()
{
  double d, e;
  char c[sizeof(double)];

  char* p_d = static_cast<char*>(static_cast<void*>(&d));
  char* p_e = static_cast<char*>(static_cast<void*>(&e));

  d = get_a_random_double();

  // double -> chars
  std::copy(p_d, p_d+sizeof(double), c);

  // chars -> double
  std::copy(c, c+sizeof(double), p_e);

  assert(e == d);
}

这个标准是否保证始终满足断言?

编辑:是的!参见 C++11 3.9/2“类型”

下一个我尝试在不使用联合的情况下通过 int64_t 传输双槽的情况如何。

我知道 int64_t 不是 c++03 的一部分,所以让我们来谈谈 c++11。

再次注意,我假设 sizeof(double) * CHAR_BITS

int main()
{
  double d, e;
  std::int64_t i, j;
  char c[sizeof(std::int64_t)];

  char* p_d = static_cast<char*>(static_cast<void*>(&d));
  char* p_e = static_cast<char*>(static_cast<void*>(&e));
  char* p_i = static_cast<char*>(static_cast<void*>(&i));
  char* p_j = static_cast<char*>(static_cast<void*>(&j));

  d = get_a_random_double();

  // double -> chars -> std::int64_t
  std::copy(p_d, p_d+sizeof(double), c);
  std::copy(c, c+sizeof(std::int64_t), p_i);

  // std::int64_t -> std::int64_t
  j = i; // <------------ Are all bits preserved here?

  // std::int64_t -> chars -> double
  std::copy(p_j, p_j+sizeof(std::int64_t), c);
  std::copy(c, c+sizeof(double), p_e);

  assert(e == d);
}

和以前一样,我的一个担忧是复制标准整数类型之一 (std::int64_t) 是否会“损坏”某些位(例如,一些不参与整数值表示的位)。或者,整数赋值是否保证总是忠实地复制整数占用的字节的所有位?

【问题讨论】:

    标签: c++ double unions type-punning


    【解决方案1】:

    不,实际上这是未定义的行为:

    u u1;
    u1.m_double = d;
    
    u u2;
    u2.m_int = u1.m_int;
    

    你不能设置u1.m_double和读取u1.m_int

    一次只允许一名活跃的工会成员。

    【讨论】:

    • 这可能是未定义的行为。但是,通过字符序列传输双精度是定义的行为。参见 C++11 3.9/2“类型”
    • 可能值得注意的是,这种行为是在 C(C99 和 C11)中定义的。 stackoverflow.com/questions/11639947
    【解决方案2】:

    语言本身并不能保证您可以使用除charunsigned char 指针之外的任何其他类型来访问其他数据类型——即使这样也是相当受限的。

    这完全取决于您希望代码的可移植性。有些机器处理浮点数的方法很奇怪,并且将它们四处转换会做坏事。

    在实践中,大多数处理器完全可以满足您刚刚所做的工作,并且大多数编译器都非常乐意生成“正确”代码来覆盖 64 位浮点数和 64 位整数,并且那样运输。

    当您尝试在 DSP、旧的 IBM 硬件、Cray YMP 或其他有点不寻常的东西上运行它时,问题就来了。或者,如果您开始使用未经测试的编译器。

    因此,在某种程度上,它归结为“您需要它的便携性”,以及您的软件对此进行了多好的测试,不会像一个奇怪的错误一样出现这个名字,而且只有在可以被 7 和 3 整除的日子,还有满月? [不可避免地,只有您最重要的客户在您的时区大约 12 小时的国家/地区]。

    【讨论】:

      【解决方案3】:

      所有大小固定的类型都是c++11。在此之前,该标准只定义了变量必须保持的最小位数(其中大多数只保证至少与前一个实际一样大......)。所以 std::int64_t 在 c++03 中是未定义的,你不能依赖任何大小的其他整数(包括字符!)。


      此外,正如 Luchian Grigore 指出的那样:(任何)标准都没有定义向一个工会成员写入数据并从另一个工会成员读取数据。然而,虽然不能保证您编写的代码能够正常工作,但大多数编译器会按照您的预期方式对其进行解释。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-15
        • 2021-04-02
        • 1970-01-01
        • 2010-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多