代码之家  ›  专栏  ›  技术社区  ›  Galaxy

为什么将变量移位超过其宽度(以位为单位)会变成零?

  •  1
  • Galaxy  · 技术社区  · 6 年前

    这个问题的灵感来自stackoverflow的其他问题。今天,在浏览stackoverflow时,我遇到了一个问题:将变量的位移一个值k,该值是该变量的宽度(以位为单位)。这意味着将32位int移位32位或更多位。

    Left shift an integer by 32 bits

    Unexpected C/C++ bitwise shift operators outcome

    从这些问题中,很明显,如果我们试图将一个数字移位k位,即>=变量的位宽度,则只取最低有效的log2k位。对于32位int,最低有效的5位被屏蔽,并被视为移位量。

    所以一般来说,如果w=变量的宽度,单位是比特, x >> k 变成 x >> (k % w) 为了一个 int ,这是 x >> (k % 32) 是的。

    计数被屏蔽为5位,这将计数范围限制为0到31。

    所以我写了一个小程序来观察理论上应该产生的行为。我在评论中写了结果的移位量%32。

    #include <stdio.h>
    #include <stdlib.h>
    
    #define PRINT_INT_HEX(x) printf("%s\t%#.8x\n", #x, x);
    
    int main(void)
    {
        printf("==============================\n");
        printf("Testing x << k, x >> k, where k >= w\n");
    
        int      lval = 0xFEDCBA98 << 32;
        //int      lval = 0xFEDCBA98 << 0;
    
        int      aval = 0xFEDCBA89 >> 36;
        //int      aval = 0xFEDCBA89 >> 4;
    
        unsigned uval = 0xFEDCBA89 >> 40;
        //unsigned uval = 0xFEDCBA89 >> 8;
    
        PRINT_INT_HEX(lval)
        PRINT_INT_HEX(aval)
        PRINT_INT_HEX(uval)
    
        putchar('\n');
    
        return EXIT_SUCCESS;
    }
    

    并且输出与移位指令的预期行为不匹配!

    ==============================
    Testing x << k, x >> k, where k >= w
    lval    00000000 
    aval    00000000
    uval    00000000
    

    =====================================================================

    实际上,我对java有点困惑。在C/C++中,用大于位宽度的位数移动int,可能会减少k %,但这不是C标准保证的。没有规则说这种行为应该一直发生。这是未定义的行为。

    然而,Java就是这样。这是Java编程语言的一条规则。

    Bitshift operators description in Java language specification

    Weird result of Java Integer left shift

    java : shift distance for int restricted to 31 bits

    2 回复  |  直到 6 年前
        1
  •  2
  •   dbush    6 年前

    链接的问题特别指出,如果移位量大于被移位类型的位宽度,则调用 undefined behavior ,标准将其定义为“使用不可移植或错误的程序结构或错误的数据时的行为,本国际标准对此不作任何要求”

    当您调用未定义的行为时,任何事情都可能发生。程序可能会崩溃,可能会输出奇怪的结果,或者看起来工作正常。此外,如果在同一个编译器上使用不同的编译器或不同的优化设置,未定义的行为表现形式本身可能会发生更改。

    C标准对第6.5.7P3节中的按位移位运算符规定如下:

    对每个操作数执行整数提升。这个 结果的类型是提升的左操作数的类型。 如果 右操作数的值为负或大于 或等于提升的左操作数的宽度,则行为为 未定义。

    在这种情况下,编译器可能会减少按您建议的位宽度进行模移位的量,也可能会将其视为按该量进行数学移位,从而导致所有位都为0。因为标准没有指定行为,所以两者都是有效的结果。

        2
  •  1
  •   Bo Persson tox    6 年前

    不确定的一个原因是8086,最初的x86, 屏蔽移位计数中的任何位。相反,它实际执行的移位,使用每个位置一个时钟滴答。

    英特尔随后意识到,允许一个移位指令有255+个时钟周期可能不是一个好主意。例如,它们可能考虑了最大中断响应时间。

    从我以前的80286手册:

    为了减少最大执行时间,IAPX 286不允许移位计数大于31。如果尝试的移位计数大于31,则只使用移位计数的后五位。iapx 86使用移位计数的所有8位。

    对于PC/XT和PC/AT上的同一个程序,结果是不同的。

    那么,语言标准应该怎么说呢?

    Java通过不使用底层硬件解决了这个问题。相反,C选择说效果还不清楚。