C++20 的分支需要二进制补码

Ramifications of C++20 requiring two's complement

C++20 将指定 signed integral types must use two's complement。考虑到(实际上?)每个实现目前都使用二进制补码,这似乎不是什么大变化。

但我想知道此更改是否会使某些 "undefined behaviors" 变为 "implementation defined" 甚至 "defined."

考虑一下绝对值函数 std::abs(int) 及其一些重载。 C++ 标准通过引用 C 标准包含此函数,该标准表示如果无法表示结果,则行为未定义。

在二进制补码中,INT_MIN没有正对应:

abs(INT_MIN) == -INT_MIN == undefined behavior

符号量级表示有:

-INT_MIN == INT_MAX

因此 abs() 留下一些未定义的行为似乎是合理的。

一旦需要二进制补码,abs(INT_MIN) 的行为可以完全指定或至少定义实现,而没有任何向后兼容性问题,这似乎是有道理的。但我没有看到任何此类更改提议。

我看到的唯一缺点是 C++ 标准需要明确指定 abs(),而不是引用 C 标准对 abs() 的描述。 (据我所知,C 并没有强制补码。)

这难道不是委员会的优先事项,还是仍然有理由不利用两者的互补授权提供的简化和确定性?

委员会考虑的 specific questions 之一是如何处理 -INT_MIN,该投票的结果是:

addition / subtraction / multiplication and -INT_MIN overflow is currently undefined behavior, it should instead be:

4: wrap
6: wrap or trap
5: intermediate values are mathematical integers
14: status quo (remain undefined behavior)

这是明确考虑过的,人们认为最好的选择是保持未定义的行为。

为了澄清 "intermediate values are mathematical integers",论文的另一部分澄清了 (int)a + (int)b > INT_MAX 可能是正确的。


请注意,在这些情况下,实现可以根据自己的选择自由定义特定行为。不知道他们有没有。

编写 C89 的委员会故意避免对质量实现 "should" 在实际情况下所做的事情做出任何判断。已发布的基本原理表明,他们希望实现在超出标准要求的情况下表现有用(并且在整数溢出的情况下,甚至记录了一些非常具体的期望),但无论出于何种原因,委员会故意避免在标准中说出这样的话本身。

当后来的 C 或 C++ 委员会添加新功能时,他们愿意考虑它们可能在某些平台上受支持而在其他平台上不受支持的可能性,但几乎从未努力重新审视标准是否适用的问题应该认识到许多实现会以相同有用和一致的方式处理代码的情况,即使标准没有强加任何要求,并提供一种方法,程序可以通过这种方法测试实现是否支持这种行为,拒绝编译不支持的行为t,并定义了那些行为的行为。

最终效果是:如果 x*y 的算术值介于 INT_MAX+1uUINT_MAX 之间,unsigned mul_mod_65536(unsigned short x, unsigned short y) { return (x*y) & 0xFFFFu; } 可能会任意破坏调用代码的行为尽管标准的作者表示他们希望大多数实现都能一致地处理这种情况。最近的标准消除了 C89 的作者预期某些实现可能会奇怪地处理上述功能的主要原因,但这并不意味着实现没有决定以 C89 的作者永远不会有的方式奇怪地对待它想象中的,并且永远不会故意允许。