有符号和无符号之间的减法,然后除法

Subtraction between signed and unsigned followed by division

本文关键字:然后 除法 符号 无符号 之间      更新时间:2023-10-16

下面的结果让我很困惑:

int i1 = 20-80u;    // -60
int i2 = 20-80;     // -60
int i3 =(20-80u)/2; // 2147483618
int i4 =(20-80)/2;  // -30
int i5 =i1/2;       // -30
  1. i3应该计算为(20u-80u)/2,而不是(20-80u)/2
  2. 假定i3i5相同。

在有符号整型和无符号整型之间进行算术运算,将产生一个无符号整型。

因此,20 - 80u产生的unsigned结果相当于-60:如果unsigned int是32位类型,则结果为4294967236。

顺便说一下,把它赋值给i1会产生一个实现定义的结果,因为这个数字太大而无法匹配。获得-60是典型的,但不能保证。
int i1 = 20-80u;    // -60

这有微妙的恶魔!操作数是不同的,所以必须进行转换。两个操作数都被转换为一个通用类型(在本例中是unsigned int)。结果,这将是一个大的unsigned int值(60小于UINT_MAX + 1如果我的计算是正确的)将被转换为int之前,它被存储在i1。由于该值超出了int的范围,因此结果将是实现定义的,可能是陷阱表示,因此当您尝试使用它时可能会导致未定义的行为。但是,在您的情况下,它恰好转换为-60


int i3 =(20-80u)/2; // 2147483618

继续第一个例子,我的猜测是20-80u的结果将比UINT_MAX + 1小60。如果UINT_MAX是4294967295 (UINT_MAX的通用值),这意味着20-80u4294967236…和4294967236 / 2是2147483618.


对于i2和其他的,不应该有什么意外。它们遵循传统的数学计算,没有转换、截断、溢出或其他实现定义的行为。

二进制算术运算符将对其操作数执行通常的算术转换,使其成为公共类型。

对于i1i3i5,通用类型将是unsigned int,因此结果也将是unsigned int。无符号数将通过模运算进行换行,因此减去稍大的无符号值将得到一个接近Unsigned int max的数字,该数字无法用int表示。

所以在i1的情况下,我们最终使用实现定义的转换,因为值不能表示。在i3除以2的情况下,unsigned值又回到了int的范围内,因此我们在转换后得到了一个大的signed int值。

c++标准草案的相关章节如下:Section 5.7 [expr.add]:

加性运算符+和-从左到右分组。执行通常的算术转换算术或枚举类型的操作数。

通常的算术转换在5节中介绍,它说:

许多要求操作数为算术或枚举类型的二元操作符会导致转换并产生结果结果以类似的方式类型。目的是产生一个公共类型,这也是结果的类型。这种模式称为常规算术转换,定义如下:

[…]

  • 否则,如果无符号整数类型的操作数的排名大于等于另一个操作数的类型为秩,具有符号整型的操作数应转换为无符号整型操作数的类型

,对于不能用符号类型表示的值的转换,参见4.7 [conv.integral]:

如果目标类型是带符号的,如果该值可以用目标类型(和)表示,则该值不变位域宽度);否则,该值为实现定义。

,对于无符号整数服从模运算章节3.9.1 [basic.fundamental]:

无符号整数应遵循算术模数2n的法则,其中n为该值的位数表示特定大小的整数。48