有符号整数现在在左移方面有不同的表现吗?

时间:2019-01-18 14:08:56

标签: c++ c++20

在c ++ 20中,现在将有符号整数定义为使用二进制补码,
参见http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r3.html

这是一个可喜的变化,但是其中一个要点引起了我的注意:

  

更改对有符号整数类型的左移会产生相同的结果   在相应的无符号整数类型上左移。

这似乎是一个奇怪的变化。这不会移开符号位吗?

2 个答案:

答案 0 :(得分:3)

带符号的左移(E1 << E2)的C ++ 17文字是:

  

否则,如果E1具有带符号类型且非负值,并且E1×2 E2 在结果类型的相应无符号类型中可表示,则将该值转换为结果类型,是结果值;否则,行为是不确定的。

带符号和无符号整数的C ++ 20措辞为:

  

E1 << E2的值是与E1×2 E2 模2 N 一致的唯一值,其中N是结果类型的范围指数

这些结果基本上是相同的;区别在于,如果E1为负,则现在可以使用。 “范围指数”基本上是该类型中的位数(带符号的16位整数的范围指数为16)。

左移并没有移到符号位,而且仍然没有。

答案 1 :(得分:2)

是的,C ++ 20改变了左移有符号整数的行为。

在C ++ 17中,将正有符号整数左移到符号位将调用定义的实现行为。 1 示例:

int i = INT_MAX;
int j = i << 1;    // implementation defined behavior with std < C++20

C ++ 20将其更改为定义行为,因为它强制使用two's complement表示有符号整数。 2,3

对于C ++ 17,移位负号整数会调用 undefined 行为。 1 示例:

int i = -1;
int j = i << 1;    // undefined behavior with std < C++20

在C ++ 20中,这也发生了变化,并且此操作现在还调用了定义的行为。 3

这似乎是一个奇怪的变化。这不会移开符号位吗?

是的,有符号的左移将符号位移开。示例:

int i = 1 << (sizeof(int)*8-1);    // C++20: defined behavior, set most significant bit
int j = i << 1;                    // C++20: defined behavior, set to 0 

将某些东西指定为未定义或实现定义的行为的主要原因是允许在不同的硬件上进行有效的实现。

如今,由于所有CPU都实现了two's complement,因此C ++标准很自然地规定了它。而且,如果您要求使用二进制补码,那么您必须做出上述操作定义的行为,因为这也是所有二进制补码指令集体系结构(ISA)中左移的行为。

IOW,将其实现定义为未定义并不会为您带来任何好处。

或者,如果您喜欢以前的未定义行为,为什么还要关心是否将其更改为已定义行为呢?您仍然可以像以前一样避免此操作。您无需更改代码。


1

E1 << E2的值是E1左移E2位的位置;空出的位为零。如果E1具有未签名 类型,结果的值是E1 × 2**E2,比以 结果类型。否则,如果E1具有带符号的类型和非负值,并且E1 × 2**E2可表示的 在结果类型的相应无符号类型中,则转换为结果类型的那个值就是 结果值; 否则,该行为是未定义

C++17 final working draft,第8.8节Shift运算符[expr.shift],第2段,第132页-强调点)

2

[..]对于有符号整数类型的每个值x, 等于x模2 N的对应无符号整数类型具有相同的对应位值 它的值表示。 41)这也被称为二进制补码表示法。 [..]

C++20 latest working draft,第6.8.1节基本类型[basic.fundamental],第3页,第66页)

3

E1 << E2的值是E1 × 2**E2 modulo 2**N的唯一值congruent,其中N是宽度的宽度。 结果的类型。 [注意:E1是E2左移位;空出的位为零。 —尾注]

C++20 latest working draft,第7.6.7节Shift运算符[expr.shift],第2页,第129页,链接矿)