无符号算术和整数溢出
Unsigned arithmetic and integer overflow
我试图理解算术溢出。假设我有以下内容
unsigned long long x;
unsigned int y, z;
x = y*z;
y*z可能导致整数溢出。将其中一个操作数转换为unsigned long long是否可以缓解此问题?64位操作数与32位操作数相乘的预期结果是什么?
您显然假设unsigned int
是32位而unsigned long long
是64位。它们不必是,让我们这样假设。
由32位操作数转换而来的64位操作数仍然适合32位。因此,在y*(unsigned long long)z
中,每个操作数首先被提升到unsigned long long
,结果被计算为unsigned long long
并且不会"溢出",因为它是两个量值的乘法,每个量值都适合32位。
(同样,在C标准的词汇表中,无符号操作不会"溢出"。溢出是在目标类型边界之外产生结果的未定义行为。无符号操作所做的是"绕行")。
unsigned long long x;
unsigned int y, z;
x = y*z;
表达式y*z
的求值不受其出现的上下文的影响。它将两个unsigned int
值相乘,得到unsigned int
结果。如果数学结果不能表示为unsigned int
值,则结果将绕行。然后,赋值操作隐式地将(可能被截断的)结果从unsigned int
转换为unsigned long long
。
如果您想要一个产生unsigned long long
结果的乘法,您需要显式地转换一个或两个操作数:
x = (unsigned long long)y * z;
或者更明确的:
x = (unsigned long long)y * (unsigned long long)z;
C的*
乘法运算符仅对两个相同类型的操作数应用。正因为如此,当你给它不同类型的操作数时,它们会在执行乘法运算之前被转换成某种通用类型。当您混合使用有符号和无符号类型时,规则可能有点复杂,但在这种情况下,如果将unsigned long long
与unsigned int
相乘,unsigned int
操作数将提升为unsigned long long
。
如果 unsigned long long
的宽度至少是unsigned int
的两倍,那么结果既不会溢出也不会绕行,因为,例如,64位的unsigned long long
可以保存任意两个32位unsigned int
值相乘的结果。但是如果你在一个系统上,例如,int
和long long
都是64位宽的,你仍然可以使用溢出环绕,给你x
的结果不等于y
和z
的数学乘积。
如果一个操作数比另一个更宽,编译器应该(或表现得好像是)将两个操作数转换为相同的大小,因此将其中一个转换为更大的大小将产生正确的行为。
在C和c++标准中指定。c++ 11标准(n3337草案)在第五章声明9中是这样说的:
…如果两个操作数都是有符号整数类型或都是无符号的整数类型,将操作数与该类型的较小整数转换
将Rank转换为Rank更高的操作数的类型。
有几个页面描述了所有的转换和东西,但这是定义这个特定表达式的行为。
- 整数溢出,最大值为 pow(10,19)
- 模函数,避免C++整数溢出
- 优化正在杀死我在 clang 6 中的整数溢出检查
- 从双精度转换为整数的显式类型是否始终检查整数溢出?
- 运行时错误:有符号整数溢出:964632435 * 10 无法在类型 'int' 中表示
- 我很难理解这些关于检测整数溢出的评论
- 检测 32 位整数溢出
- 如何在没有整数溢出的情况下找到n%(k*k)
- C 中俄罗斯农民算法中的整数溢出
- 检查Android NDK COD中的整数溢出
- 对于特定情况的整数溢出似乎是由整数溢出引起的错误
- 如何修复整数溢出警告
- 整数溢出 C++ 即使使用“无符号长整型”也是如此
- 整数溢出是否会影响其他变量
- 为什么功能不给整数溢出
- 有符号整数溢出、内部函数和未定义的行为
- 整数溢出不会发生:它们从0重新启动
- 以下程序中最大 int 1000 000(在 int 范围内)的整数溢出
- 为什么Turbo C Wraparound每次签名的整数溢出,尽管签名的整数溢出是不确定的
- 编译器是否有可能检测整数溢出或其他数据类型溢出的可能性