【问题标题】:Does C++20 well-define left shift for signed integers that "overflow"?C++20 是否为“溢出”的有符号整数定义了左移?
【发布时间】:2019-08-23 10:15:24
【问题描述】:

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

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

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

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

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

事实上,现在不再要求E1 为非负数。因此,-1 << 1 似乎被定义为-2。也是这样吗?

【问题讨论】:

  • “事实上,现在不再要求E2 为非负数”。在您提供的链接中,有注释“如果右操作数为负,则行为未定义”。你的意思是E1,就像你给出的例子一样?或者,自从您发布问题后,情况可能发生了变化?
  • @deltacrux 打错字了,谢谢提醒。
  • 没问题,感谢您的澄清!

标签: c++ language-lawyer bit-shift c++20


【解决方案1】:

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

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

如果我们查看"p0907R1 Signed Integers are Two’s Complement" proposal,我们会发现一个与“唯一表示”类似的短语,这更清楚了:

从有符号到无符号的转换总是明确定义的:结果 是目标类型的唯一值,与 源整数模 2N.

那么,我想结果定义为-2。这是解释吗 对吗?

是的

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

我想这个变化与强制性的 2-补码有关 负符号整数的表示。

正确。与无符号类型的情况一样,现在也有符号类型,它们在数学上表示等价类(嗯,我不清楚这在多大程度上是正确的,因为看起来他们仍然希望保持一些 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 模 2 N 的无符号整数类型具有 其值表示中对应位的值相同。

我认为我们还需要:

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

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

因此,-1

 (signed)-1 << 1 = 
 4294967295 << 1 = 
 4294967295 * 2 ^ 1 mod 4294967296 = 
 8589934590 mod 4294967296 = 
 4294967294 = 
 (signed)-2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    • 2016-04-07
    相关资源
    最近更新 更多