ChatGPT解决这个技术问题 Extra ChatGPT

有符号整数溢出在 C++ 中仍然是未定义的行为吗?

我们知道,signed integer overflow is undefined behavior。但是 C++11 cstdint 文档中有一些有趣的东西:

有符号整数类型,宽度分别为 8、16、32 和 64 位,没有填充位,负值使用 2 的补码(仅在实现直接支持该类型时提供)

See link

这是我的问题:既然标准明确规定 int8_tint16_tint32_tint64_t 负数是 2 的补码,这些类型的溢出仍然是未定义的行为吗?

编辑我检查了 C++11 和 C11 标准,这是我发现的:

C++11,第 18.4.1 节:

头文件定义了与 C 标准中的 7.20 相同的所有函数、类型和宏。

C11,§7.20.1.1:

typedef 名称 intN_t 指定宽度为 N、无填充位和二进制补码表示的有符号整数类型。因此, int8_t 表示这种宽度正好为 8 位的有符号整数类型。

永远不要忘记 C++ 的唯一主要文档是标准。其他一切,甚至像 CppReference 这样的 wiki,都是次要来源。这并不意味着它是错误的。只是不完全可靠。
我希望它是 UB,在 C 中这些类型没有例外,我不明白为什么 C++ 会添加一个。
我有点困惑:句子中的动词在哪里“有符号整数类型,宽度分别为 8、16、32 和 64 位,没有填充位,对负值使用 2 的补码(仅在实现直接支持时提供)类型)?”是不是有点缺?这是什么意思?
C++11 基于 C99,而不是 C11。但这无论如何都不重要

A
Andy Prowl

这些类型的溢出仍然是未定义的行为吗?

是的。根据 C++11 标准的第 5/4 段(关于一般的任何表达式):

如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。 [...]

对这些有符号类型使用二进制补码表示这一事实并不意味着在评估这些类型的表达式时使用算术模 2^n。

另一方面,关于无符号算术,标准明确规定(第 3.9.1/4 段):

声明为无符号的无符号整数应遵守算术模 2^n 的定律,其中 n 是该特定整数大小的值表示中的位数

这意味着无符号算术运算的结果始终是“数学定义的”,并且结果始终在可表示的范围内;因此,5/4 不适用。脚注 46 解释了这一点:

46) 这意味着无符号算术不会溢出,因为不能由得到的无符号整数类型表示的结果以比得到的无符号整数类型可以表示的最大值大一的数字为模减少。


这一段也暗示无符号溢出是未定义的,事实并非如此。
@Archie:不是真的,因为无符号值是以无符号范围为模定义的。
@Archie:我试图澄清,但基本上你从 LightnessRacesinOrbit 得到了答案
如果由于模计算而不能发生无符号溢出,是否定义无符号溢出实际上并不重要......
有一些无符号运算的结果不是“数学定义的”——尤其是除以零——所以你的措辞可能不是你在那个句子中的意思。 ITYM 认为,当结果在数学上定义时,它也在 C++ 中定义。
e
ecatmur

仅仅因为一个类型被定义为使用 2s 补码表示,它并没有遵循该类型中的算术溢出被定义。

有符号算术溢出的未定义行为用于启用优化;例如,编译器可以假设 if a > b then a + 1 > b 也;这不适用于需要执行第二次检查的无符号算术,因为 a + 1 可能会回绕到 0。此外,某些平台可以在算术溢出时生成陷阱信号(参见例如 http://www.gnu.org/software/libc/manual/html_node/Program-Error-Signals.html);该标准继续允许这种情况发生。


可能值得注意的是,许多人“担心”更多关于陷阱的可能性,但编译器假设实际上更隐蔽(我希望在实现定义和未定义行为之间存在一个类别的原因之一 - 不像实现定义的行为这需要特定的实现以一致的记录方式做某事,我想要一个“实现受限”的行为,它需要实现来指定由于某事而可能发生的所有事情(规范可以明确包括未定义的行为,但是.. .
...在实际可行的情况下,将鼓励实施更加具体)。在二进制补码自然“换行”的硬件上,没有合理的理由让代码需要换行的整数结果来执行许多指令,试图在没有整数溢出的情况下执行硬件可以在一个或两个指令中完成的计算.
@supercat 实际上,想要包装结果的代码可以(在 2 的补码 CPU 上)只需将操作数转换为相应的无符号类型并执行操作(然后转换回,获取实现定义的值):这适用于加法、减法和乘法.唯一的问题是除法、模数和 abs 等函数。对于那些工作时的操作,它不需要比签名算术更多的指令。
@Ruslan:在代码需要精确包装的结果的情况下,强制转换为 unsigned 会很丑陋,但不一定会生成额外的代码。一个更大的问题是需要快速识别“潜在有趣”候选人的代码,这将花费大部分时间拒绝不感兴趣的候选人。如果让编译器可以自由地任意保留或丢弃带符号整数值的额外精度,但要求强制转换回整数类型会截断任何此类精度,这将启用通过溢出 UB 实现的大多数有用优化, ...
...但允许需要精确包装的代码使用一个强制转换而不是两个(例如 (int)(x+y)>z 将比较一个包装的结果),并且还允许程序员在代码可以接受的情况下编写 x+y>z 如果没有其他副作用,则在溢出的情况下产生 0 或 1。如果 0 或 1 是同样可接受的结果,则让程序员编写而不是 (long)x+y>z(int)((unsigned)x+y)>z 将允许编译器选择后一个函数中的任何一个在任何给定的上下文中更便宜[每个在某些情况下都会更便宜案例]。
C
Community

我敢打赌。

从标准文档(pg.4 和 5):

1.3.24 未定义行为 本国际标准对其没有要求的行为 [注:当本国际标准省略任何明确的行为定义或程序使用错误构造或错误数据时,可能会出现未定义行为。允许的未定义行为的范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(有或没有发出诊断消息),到终止翻译或执行(发出的诊断消息)。许多错误的程序结构不会产生未定义的行为;他们需要被诊断出来。--结束注]