在当前的C++标准草案中,左移运算符定义如下[expr.shift]:
E1 << E2
的值是与模2^N
全等E1×2^E2
的唯一值,其中N
是结果类型的宽度。
考虑int E1 = 2^31-1 = 2'147'483'647
、E2 = 1
和int
有32位。然后有无限多个数全等到E1×2^E2 = 4'294'967'294
模2^N = 2^32
,即所有数4'294'967'294 + k×2^32
其中k
是任意整数。例如4'294'967'294
(k=0
)或-2
(k=-1
)。
我不明白这些数字中的唯一值是什么意思。它是否表示可以由生成的数据类型表示的唯一值?然后,我想结果定义为-2
.这种解释正确吗?
在C++20之前,定义是不同的,这种情况会导致未定义的行为。我想这种变化与负有符号整数的强制性 2's 补码表示有关。
事实上,现在不再要求E1
是非负数。因此,似乎-1 << 1
被定义为-2
。也对吗?
它是否意味着可以由结果表示的唯一值 数据类型
是的。全等于E1×2^E2
模2^N
的数集是无穷大的,但是在任何大小2^N
区间中只有一个值,因此在宽度N
的整数类型中只有一个值可表示。
如果我们查看"p0907R1 有符号整数是 Two's Complement"提案,我们会发现一个类似的短语,其中包含"唯一表示",这使其更加清晰:
从有符号到无符号的转换始终是明确定义的:结果 是与源整数模 2N。
然后,我想结果定义为
-2
.这是解释吗 正确?
是的
在 x64 上,等效的 asm 指令shlx
(逻辑左移)
我想这个变化与强制性的 2 补码有关 负有符号整数的表示形式。
正确。与无符号类型的情况一样,现在也有符号类型,它们在数学上表示等价类(好吧,我不清楚这有多真实,因为看起来他们仍然希望将一些 UB 案例保留在溢出上)。
所以我们知道:
E1 = 2147483647
E2 = 1
N = sizeof(int) * CHAR_BIT = 4 * 8 = 32
让我们计算E1×2^E2 modulo 2^N
(模是除法的余数):
x = E1×2^E2 mod 2^N = 2147483647 * 2 ^ 1 mod 4294967296 = 4294967294 mod 4294967296 = 4294967294
然后我们去这里:
对于有符号整数类型的每个值 x,值 对应于 x 模 2 N 全等的无符号整数类型具有 其值表示形式中相应位的相同值。
我认为我们还需要:
有符号整数类型的值的 base-2 表示形式是 相应全等值的基数为 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