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

Posted

技术标签:

【中文标题】有符号和无符号之间的减法,然后是除法【英文标题】:Subtraction between signed and unsigned followed by division 【发布时间】:2015-12-05 06:52:58 【问题描述】:

下面的结果让我很困惑:

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
    i3 似乎被计算为 (20u-80u)/2,而不是 (20-80u)/2 据说i3i5是一样的。

【问题讨论】:

【参考方案1】:
int i1 = 20-80u;    // -60

这有微妙的恶魔!操作数不同,因此需要转换。两个操作数都转换为通用类型(在本例中为unsigned int)。结果将是一个大的unsigned int 值(如果我的计算正确,则比UINT_MAX + 1 小60)将在存储到i1 之前转换为int。由于该值超出了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等人,应该不会有什么意外吧。它们遵循传统的数学计算,没有任何转换、截断、溢出或其他实现定义的行为。

【讨论】:

所以如果我理解正确的话,将 -1 转换为无符号是明确定义的,它是 UINT_MAX。但是,如果您随后将 UINT_MAX 转换回 int 是否突然定义了实现?并且不能是-1? 很好的回答日:)【参考方案2】:

二元算术运算符将对它们的操作数执行usual arithmetic conversions 以将它们带入一个通用类型。

i1i3i5 的情况下,公共类型将为unsigned int,因此结果也是unsigned int。无符号数将通过模运算进行换行,因此减去稍大的无符号值将导致接近无符号 int 最大值的数字,该数不能用 int 表示。

所以在i1 的情况下,我们最终得到了实现定义的转换,因为无法表示值。在i3 除以2 的情况下,无符号值又回到了 int 的范围内,因此我们在转换后得到一个大的有符号 int 值。

C++ 标准草案的相关部分如下。部分5.7[expr.add]

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

5 部分介绍了常用的算术转换,它说:

许多期望算术或枚举类型操作数的二元运算符会导致转换和产生 结果类型以类似的方式。目的是产生一个通用类型,这也是结果的类型。 这种模式称为通常的算术转换,定义如下:

[...]

否则,如果具有无符号整数类型的操作数的秩大于或等于 其他操作数类型的等级,带符号整数类型的操作数应转换为 无符号整数类型的操作数的类型。

对于从无法表示为有符号类型的值的转换,4.7 部分 [conv.integral]

如果目标类型是有符号的,如果它可以在目标类型中表示,则值不变(并且 位域宽度);否则,该值是实现定义的。

对于无符号整数遵循模运算部分3.9.1 [basic.fundamental]

无符号整数应遵循算术模 2n 的定律,其中 n 是值中的位数 表示特定大小的整数.48

【讨论】:

@Hurkyl:该死,我今天站着睡觉,我破坏了无符号溢出和从无符号到有符号的转换(后者是实现定义的)。我会自毁我的评论...【参考方案3】:

IIRC,有符号和无符号整数之间的算术运算将产生无符号结果。

因此,20 - 80u 产生等同于 -60 的无符号结果:如果 unsigned int 是 32 位类型,则结果为 4294967236。

顺便说一句,将其分配给 i1 会产生 实现定义 结果,因为该数字太大而无法容纳。获取-60 是典型的,但不能保证。

【讨论】:

顺便说一下,将该值分配给 i1 是未定义的行为您确定吗?我教过从 unsigned int 到有符号 int 的转换对于 unsigned int 的所有值都有很好的定义。 这里没有有符号整数溢出。有转换。见conv.integral。 @rozina:嗯,我以前从未见过转换在这方面的工作方式不同。固定

以上是关于有符号和无符号之间的减法,然后是除法的主要内容,如果未能解决你的问题,请参考以下文章

c中的有符号字符和无符号字符之间的区别

如何忽略“有符号和无符号整数表达式之间的比较”?

有符号和无符号字符之间的比较

Arduino 在 uint32_t 和无符号字符之间转换

有符号/无符号字符之间的区别[重复]

JAVA中类型(char、long、int.....一共八个)有符号和无符号是啥意思