cpp和java中负数的右移行为是否未定义?

cpp和java中负数的右移行为是否未定义?,java,c++,undefined-behavior,c++17,Java,C++,Undefined Behavior,C++17,为了优化我的cpp代码,我尝试在某些情况下使用右移。下面是一个例子: 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

为了优化我的cpp代码,我尝试在某些情况下使用右移。下面是一个例子:

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
1111…1 31,1用于填充空位置
我只是想知道这种行为是否有明确的定义?


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

是。它是实现定义的

根据定义右移的C++03 5.8/3

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


有关更多信息,请参见此。

Java中,
的行为对于负数定义良好(请参见下文)

C++中,对于负数,
>
的行为是未定义的(请参阅)


引用Java语言规范:

n>s的值是n个右移位的s位位置,带有符号扩展。结果值为地板(n/2s)。对于n的非负值,这相当于将整数除法运算符/计算出的整数除法截断为s的幂

n>>s的值是n个右移位的s位位置,具有零扩展,其中:

  • 如果n为正,则结果与n>>s的结果相同


  • 如果n为负数且左侧操作数的类型为
    int
    ,则结果等于表达式(n>>s)+(2>s)+(2L默认情况下,它是带符号的int。范围是-32767到32767,按位范围是-111111111到+111111111。左边的第一位作为负数或正数指示符。所有算术运算都将在2的补码方法中完成。通常负int在两个补码方法中表示,例如w-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内移动到最高位位置,然后在2s补码表示上,它将表现为算术移位-右移N的结果将与除以2N的结果相同,向负无穷大舍入。因此-1的移位为-1 现在换一个号码 例如 如果有8位2s补码二进制数,则表示-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。

    可能的重复是左移(在Java中,绝对不是未定义的。
    >
    有符号扩展,
    >
    没有符号扩展。请参阅Java语言规范或其最终来源,“Java中如何?”-为什么要停在Java上,用C#和Rust来询问它。你能读一下这个问题中被接受的答案的第五条评论吗?我不太明白他的意思…IB或UB…@Yves,如果你的意思是“右移负符号整数就是IB”,没有什么可以添加到它来让它更清晰。@molbdnilo现在,我的理解是:不要移动任何位来覆盖符号位,因为它是UB,这意味着左移一个大的有符号整数是危险的。而右移是IB…@Yves-这是危险的,不是因为它是UB,而是因为结果可能在di之间有所不同对于C++来说,行为是定义的,但是定义可以在不同系统之间变化。假设它做硬件所做的事情。@ BoPersson,这意味着如果你不知道你的代码将在哪里运行,它是不确定的,但是我得到你的区分。@安德烈亚斯:未定义的行为被现代编译器解释为邀请。on以“optimization”的名义进行无意义的行为,这与生成已定义或未指定的指令值完全不同。例如,给定
    unsigned mulMod65536(unsigned short x,unsigned short y){return(x*y)&0xFFFF;}
    ,如果
    x*y
    超过
    0x7FFFFFFF
    ,你可能看不出有任何理由会发生任何奇怪的事情,但是在gcc中,如果
    x*y
    不适合
    int
    ,这样的函数有时会产生奇怪的副作用。定义的实现不允许这样的许可证。@supercat“未定义”简单地说,规范/API没有定义/指定行为表示随机/任意,只是指一个实现可以做它喜欢做的事情,并且可以随时自由更改它喜欢的东西。这意味着,作为该功能的用户,我们不能依赖任何特定的结果。问题是没有实现上下文,也不知道确切的实现,“实现定义”实际上意味着与“未定义”相同,即你不知道会发生什么,因此就这个问题而言,
    >
    是未定义的。@Andreas:一些理智的编译器会认识到,以“环境特有的文档化方式”行事是有意义的[标准中列出的UB建议的典型结果之一]当底层平台具有“自然”行为时,不能依靠现代的编译器来实现。对
    (i*65535)和65535u
    中的
    进行评估(int i=32767;针对新手的初始答案…仅针对