sha*_*oth 16 c++ shift language-lawyer
根据C++ 03,5.8/2,左移定义如下:
E1 << E2的值是E1(解释为位模式)左移E2位位置; 空位是零填充的.如果E1具有无符号类型,则结果的值为E1乘以上升到功率E2的数量2,如果E1的类型为无符号长,则减少模ULONG_MAX + 1,否则为UINT_MAX + 1.
让我困扰的是,明确提到了无符号类型,但完全忽略了已签名的类型.将其与定义右移的5.8/3进行比较:
E1 >> E2的值是E1右移E2位位置.如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1的商除以提升到功率E2的数量2的积分.如果E1具有带符号类型和负值,则结果值是实现定义的.
在5.8/3中明确提到了有符号和无符号,甚至签名持有非负数和签名持有负值也单独提及.
AFAIK在C++ Standard中未明确定义某些内容时,行为未定义.我也看过这个问题,但它侧重于C和C++之间的差异,似乎并没有得到每个人都会同意的答案.
左移是C++ 03中定义的有符号整数吗?
5.8/2表示它将它解释为一个位模式,如果由于某种原因你的实现不使用2的补码,或者如果你的编译器第二次猜测你(他们没有),那么它只是依赖于实现.C++ 11更明确,但同样的说法.
有符号整数使用所谓的2的补码.基本上,如果你将有符号整数移位1,如果它是正数且低于2 ^(位 - 2),它将像无符号一样工作.如果它高于该值但是正数,则会创建一个与原始数字无关的奇怪负数.如果它开头是负面的,你可能会得到负数,可能是正数.
例如,如果我们有一个表示-1的8位有符号整数:
11111111 // -1
Run Code Online (Sandbox Code Playgroud)
如果我们左移,我们最终会
11111110 // -2
Run Code Online (Sandbox Code Playgroud)
但是,假设我们有-120
10001000 // -120
Run Code Online (Sandbox Code Playgroud)
我们最终会结束
00010000 // 16
Run Code Online (Sandbox Code Playgroud)
显然这是不正确的!
继续,使用数字65:
01000001 // 65
Run Code Online (Sandbox Code Playgroud)
向左移,这将成为:
10000001 // -127
Run Code Online (Sandbox Code Playgroud)
等于-127.
但是,数字16:
00010000 // 16
Run Code Online (Sandbox Code Playgroud)
左移是
00100000 // 32
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,它"有时可以工作,有时不工作",但通常在您的数字低于2 ^(位-2)时有效,有时但如果高于 - (2 ^(位-2))则通常无效.也就是说,向左移动1.向左移动2,再取消一点.等等.
我想补充一点,C++ 11中的规则发生了变化.
在C++ 11,留下了负数的签署移总是未定义,即使底层机器定义它为处于范围内的值.它不是实现定义的,它是未定义的.这意味着如果你这样做,编译器可以自由地做任何想做的事情,包括意外删除你的一堆代码.这是相对于签署移权负数,这是实现定义的,这意味着它的结果取决于机床的类型.
Clang的-fsanitize=undefined模式捕获了左移负数的尝试.
| 归档时间: |
|
| 查看次数: |
5072 次 |
| 最近记录: |