2017-08-01 37 views
-8

为了优化我的cpp代码,我试图在某些情况下使用Right Shifting。这里有一个例子:
是否正确移动cpp和java中负数的未定义行为?

int main() 
{ 
    int i = (1 - 2) >> 31; // sizeof(int) == 4 
    ... 
    ... 
} 

我打印的i和我-1。这意味着如果数字为负值,它将使用1而不是0来填充空位。换句话说,-1 >> 31如下工作:

1111...1 <--- the result of (1 - 2), which is -1 
1111...1 <--- -1 >> 31, 1 is used to fill in the empty position 

我只是想知道,如果这种行为被明确定义或没有?

如果是cpp中的UB,那么在Java中呢?

+1

可能的重复[是左移(<<)在C++ 11中的一个负整数未定义行为?](https://stackoverflow.com/questions/19593938/is-left-shifting-a-negative- integer-undefined-behavior-in-c11) – user0042

+0

在Java中,绝对不是未定义的。 '>>'符号扩展名,'>>>'不。请参阅[Java™教程 - 位移和位移运算符](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html)或权威源语言Java语言规范[15.19。 Shift运算符](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.19)。 – Andreas

+1

*“在Java中怎么样?”* - 为什么停留在Java,在C#和Rust中询问它。 – StoryTeller

回答

1

。它是实现定义的。

根据C++ 03 5.8/3限定向右移位:

E1 E2 >>值是E1右移E2位位置。如果E1具有 无符号类型,或者如果E1具有带符号类型和非负值,则结果值是E1 的商的整数部分除以提高到功率E2的数量2。 如果E1有一个带符号的 类型和一个负值,则结果值为 实现定义。

欲了解更多信息,请参阅此link

+0

https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array你能读到接受的答案的第五评论在这个题?我不太明白他的意思...... IB或UB ...... – Yves

+0

@Yves如果你的意思是说“右移一个负号的整数是IB”,那么没有太多的人可以添加它使其更清晰。 – molbdnilo

+0

@molbdnilo现在,我的理解是:不要移动任何位来覆盖符号位,因为它是UB,这意味着左移一个大的有符号整数是危险的。而右移是IB ... – Yves

0

爪哇,的>>行为是负数(见下文)良好定义

C++,的>>行为是未定义负数(见answer by rsp)。


引用Java语言规范,§15.19. Shift Operators

Ñ值>>小号Ñ右移小号比特位置用符号扩展。由此产生的值是floor(n/2 s。对于非负值n,这相当于如由整数除法运算符I所计算的将整数除法截断两倍于功率s

Ñ>>>小号值是Ñ右移小号比特位置具有零扩展,其中:

  • 如果Ñ是正,那么结果与相同n >> s

  • 如果Ñ为负,左侧操作数的类型是int,那么结果是等于表达式(N >> S)+(2 < <〜多个)的。

  • 如果Ñ为负,左侧操作数的类型是long,那么结果是等于表达式(N >> S)+的(2L < <〜多个)

+0

对于C++,行为*是*定义的,但允许定义在不同系统之间变化。据说它是做硬件的。 –

+0

@BoPersson这意味着如果你不知道你的代码在哪里运行,那么它是未定义的,但我明白你的区别。 – Andreas

+0

@Andreas:未定义的行为被现代编译器解释为以“优化”名义行为无效的邀请,这与产生Implemnentation-Defined或Unspecified值完全不同。例如,给定'unsigned mulMod65536(unsigned short x,unsigned short y){return(x * y)&0xFFFF;}',如果'x * y'超过0x7FFFFFFF',可能会看不到任何奇怪的原因。 ,但是在gcc中,如果'x * y'不适合'int',这样的函数有时会产生奇怪的副作用。实施定义不允许这样的许可证。 – supercat

1

默认情况下它是带符号的int。范围是-32767至32767,位范围-11111111111111111至+111111111111111左边的第一位用作负指示符或正指示符。所有的算术运算都将以2的补码方式完成。一般来说,负int表示两个互补方法,即以你为例来表示-1如何表示。

4 Bytes = 32 bits 
0000 0000 0000 0000 0000 0000 0000 0000 
how represent 1 
0000 0000 0000 0000 0000 0000 0000 0001 
Then we invert the digits. 0 becomes 1, 1 becomes 0. 
1111 1111 1111 1111 1111 1111 1111 1110 
Then we add 1. 
1111 1111 1111 1111 1111 1111 1111 1111 
This is how -1 is represented 

负数的右移被限定在1s至移位到最高位的位置,然后在一个2的补码表示将表现为一个算术移位 - 右移位乘N的结果将与2N相除,向负无穷大舍入。所以移位-1 -1 现在采取其它数量 例如, 如果有8位二进制补二进制数表示让-3

0000 0011 
    Then we invert the digits. 
    1111 1100 
    Then we add 1. 
    1111 1101 

11111101代表-3十进制,和你执行算术右移1以给出11111110,其十进制表示-2,这与将-3除以2^1相同,给出-1.5,其向负无穷向舍入导致-2。

+0

对于一个新手来说这是一个很好的答案......并且只是为了记录:不要忘记接受答案......即使对于已经关闭的问题也适用您。现在你已经达到了这个水平,并且快乐的投票,;-) – GhostCat

相关问题