无符号算术和整数溢出

时间:2014-03-10 22:37:40

标签: c++ c long-integer integer-overflow integer-arithmetic

我试图理解算术溢出。假设我有以下内容,

unsigned long long x;
unsigned int y, z;

x = y*z;

y * z会导致整数溢出。将其中一个操作数强制转换为unsigned long long可以缓解此问题。 64位操作数与32位操作数相乘的预期结果是什么?

3 个答案:

答案 0 :(得分:5)

您显然假设unsigned int是32位且unsigned long long是64位。它们不一定是,让我们假设这一点。

通过转换32位操作数获得的64位操作数仍然适合32位。因此,在y*(unsigned long long)z中,每个操作数首先被提升为unsigned long long,结果计算为unsigned long long并且不能“溢出”,因为它是两个量化的乘法每个32位。

(另外,在C标准的词汇表中,无符号运算不会“溢出”。溢出是在目标类型的边界之外产生结果的未定义行为。无符号运算的作用是“环绕”)。

答案 1 :(得分:4)

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操作数将提升为{{ 1}}。

如果 unsigned long long的宽度至少是unsigned long long的两倍,就像在大多数系统上一样,那么结果既不会溢出也不会回绕,因为,例如,64位unsigned int可以保存任意两个32位unsigned long long值的乘法结果。但是,如果你所在的系统,例如unsigned intint都是64位宽,你仍然可以有 overflow 环绕,在{中给你一个结果{1}}这不等于long longx的数学乘积。

答案 2 :(得分:3)

如果一个操作数比另一个操作数宽,则编译器应该(或表现得好像)将两个操作数转换为相同的大小,因此将一个操作数转换为更大的操作数将产生正确的行为。

这在C和C ++标准中规定。 C ++ 11标准(n3337草案)在第五章中有说法9:

  

...如果两个操作数都有有符号整数类型或两者都有无符号   整数类型,具有较小整数转换类型的操作数   rank应转换为具有更高等级的操作数的类型。

有几页描述了所有的转换和内容,但这就是定义这个特定表达式行为的原因。

相关问题