C ++ 20是否很好地定义了“溢出”的有符号整数的左移?

问题描述 投票:9回答:2

在当前的C ++标准草案中,左移位运算符定义如下:[expr.shift]

E1 << E2的值是与E1×2^E2 modulo 2^N一致的唯一值,其中N是结果类型的宽度。

考虑具有32位的int E1 = 2^31-1 = 2'147'483'647E2 = 1int。然后有无数个数字与E1×2^E2 = 4'294'967'294 modulo 2^N = 2^32一致,即所有数字4'294'967'294 + k×2^32,其中k是一个任意整数。例如4'294'967'294k=0)或-2k=-1)。

我不明白标准的含义是这些数字中的唯一值。它是否意味着结果数据类型可以表示的唯一值?然后,我想结果被定义为-2。这种解释是否正确?

在C ++ 20之前,定义不同,这种情况会导致未定义的行为。我想这个改变与负有符号整数的强制二进制补码表示有关。

事实上,现在没有更多要求E2是非负面的。因此,似乎-1 << 1被定义为-2。那是对的吗?

c++ language-lawyer bit-shift c++20
2个回答
5
投票

它是否意味着结果数据类型可以表示的唯一值

是。与E1×2^E2模数2^N一致的数字集是无限的,但在任何大小为2^N的区间中只有一个值,因此在整数类型的宽度N中只有一个值可表示。

如果我们查看"p0907R1 Signed Integers are Two’s Complement" proposal,我们会发现一个具有“独特表示”的类似短语,这使得这一点更清晰:

从signed到unsigned的转换始终是明确定义的:结果是目标类型的唯一值,该值与源整数模2N一致。

然后,我想结果被定义为-2。这种解释是否正确?

在x64上,等效的asm指令是shlx(逻辑左移)

我想这个改变与负有符号整数的强制二补码表示有关。

正确。与无符号类型的情况一样,现在也是签名类型,它们在数学上代表等价类(好吧,我不清楚这是多少,因为看起来他们仍希望在溢出时保留一些UB情况)。


2
投票

所以我们知道:

E1 = 2147483647
E2 = 1
N = sizeof(int) * CHAR_BIT = 4 * 8 = 32

让我们计算E1×2^E2 modulo 2^Nmodulo是除法的其余部分):

x = E1×2^E2 mod 2^N = 2147483647 * 2 ^ 1 mod 4294967296 = 4294967294 mod 4294967296 = 4294967294

然后我们去here

对于有符号整数类型的每个值x,对应于x modulo 2 N的相应无符号整数类型的值在其值表示中具有相同的相应位值。

而且我认为我们还需要:

有符号整数类型的值的base-2表示是相应无符号整数类型的全等值的base-2表示。

这意味着,x = 4294967294等于x = -2signed int。所以结果将是-2

因此,似乎-1 << 1被定义为-2。它也是对的吗?

 (signed)-1 << 1 = 
 4294967295 << 1 = 
 4294967295 * 2 ^ 1 mod 4294967296 = 
 8589934590 mod 4294967296 = 
 4294967294 = 
 (signed)-2
© www.soinside.com 2019 - 2024. All rights reserved.