在当前的C ++标准草案中,左移位运算符定义如下:[expr.shift]:
E1 << E2
的值是与E1×2^E2
modulo2^N
一致的唯一值,其中N
是结果类型的宽度。
考虑具有32位的int E1 = 2^31-1 = 2'147'483'647
,E2 = 1
和int
。然后有无数个数字与E1×2^E2 = 4'294'967'294
modulo 2^N = 2^32
一致,即所有数字4'294'967'294 + k×2^32
,其中k
是一个任意整数。例如4'294'967'294
(k=0
)或-2
(k=-1
)。
我不明白标准的含义是这些数字中的唯一值。它是否意味着结果数据类型可以表示的唯一值?然后,我想结果被定义为-2
。这种解释是否正确?
在C ++ 20之前,定义不同,这种情况会导致未定义的行为。我想这个改变与负有符号整数的强制二进制补码表示有关。
事实上,现在没有更多要求E2
是非负面的。因此,似乎-1 << 1
被定义为-2
。那是对的吗?
它是否意味着结果数据类型可以表示的唯一值
是。与E1×2^E2
模数2^N
一致的数字集是无限的,但在任何大小为2^N
的区间中只有一个值,因此在整数类型的宽度N
中只有一个值可表示。
如果我们查看"p0907R1 Signed Integers are Two’s Complement" proposal,我们会发现一个具有“独特表示”的类似短语,这使得这一点更清晰:
从signed到unsigned的转换始终是明确定义的:结果是目标类型的唯一值,该值与源整数模2N一致。
然后,我想结果被定义为
-2
。这种解释是否正确?
是
在x64上,等效的asm指令是shlx
(逻辑左移)
我想这个改变与负有符号整数的强制二补码表示有关。
正确。与无符号类型的情况一样,现在也是签名类型,它们在数学上代表等价类(好吧,我不清楚这是多少,因为看起来他们仍希望在溢出时保留一些UB情况)。
所以我们知道:
E1 = 2147483647
E2 = 1
N = sizeof(int) * CHAR_BIT = 4 * 8 = 32
让我们计算E1×2^E2 modulo 2^N
(modulo是除法的其余部分):
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 = -2
的signed int
。所以结果将是-2
。
因此,似乎-1 << 1被定义为-2。它也是对的吗?
(signed)-1 << 1 =
4294967295 << 1 =
4294967295 * 2 ^ 1 mod 4294967296 =
8589934590 mod 4294967296 =
4294967294 =
(signed)-2