如何进行双块添加没有未定义的行为?

Ste*_*314 9 c c++ integer-overflow

编辑公共健康警告 - 此问题包含有关未定义行为的错误假设.见接受的答案.

在阅读了最近的博客文章后,我一直在思考避免C和C++代码中所有标准未定义假设的实用性.这是一个用C++剪切的片段,用于做无符号的128位加法......

void c_UInt64_Pair::operator+= (const c_UInt64_Pair &p)
{
  m_Low  += p.m_Low;
  m_High += p.m_High;

  if (m_Low < p.m_Low)  m_High++;
}
Run Code Online (Sandbox Code Playgroud)

这显然依赖于关于溢出行为的假设.显然,大多数机器都可以支持正确类型的二进制整数(尽管可能是从32位块或其他任何东西构建的),但优化者可能会在这里利用标准未定义的行为.也就是说,m_Low < p.m_Low条件可以通过的唯一方法是m_Low += p.m_Low溢出,这是未定义的行为,因此优化器可以合法地确定条件总是失败.在这种情况下,这个代码就被打破了.

问题是,因此......

如何在不依赖未定义行为的情况下编写上述合理有效的版本?

假设您有一个适当的64位二进制机器整数,但您有一个恶意编译器,它将始终以最坏的(或不可能的)方式解释您的未定义行为.此外,假设您没有一些特殊的内置,内在,库或其他任何为您执行此操作的内容.

EDIT 略微澄清 - 这不仅仅是关于检测溢出,而且还确保m_Low和m_High都以正确的模2 ^ 64结果结束,这也是标准未定义的.

Dav*_*ley 15

从C++ 1998标准3.9.1(4):"无符号整数,声明无符号整数,应遵守算术模2 ^ n的定律,其中n是整数特定大小的值表示中的位数." 注意,"整数"在这里指的是任何整数类型而不仅仅是int.

因此,假设那些是无符号整数,如类型中的"UInt64"所示,这是C++中定义的行为,应该按预期工作.

  • @Billy,因为无符号类型遵循模运算的规则,所以永远不会有超出可表示值范围的结果,因此定义了行为.3.9.1/4的脚注同样说明了. (4认同)
  • @Billy ONeal:我们假设Cite的引用是正确的.在任何情况下,相同的模数规则适用于C99.算术模2 ^ n总是被很好地定义,结果没有模糊性:它们总是在"0 <= x <2 ^ n"的范围内.因此,关于数学上未定义的行为的额外子句永远不会应用于无符号类型.无符号整数类型换行,但它们不会溢出. (2认同)
  • 您也可以引用C标准. (2认同)