如何在Python中对有符号和无符号值进行算术右移

nur*_*bha 9 python math bit-shift shift bitwise-operators

某些语言(例如 Java、Verilog)同时具有按位逻辑运算符(<<、>>)和算术移位运算符(<<<、>>>)。

对于无符号值,逻辑移位和算术移位具有相同的操作。假设 8'b11000101 是 8 位无符号数 197 的二进制表示,那么

8'b11000101 >>  2 => 8'b00110001
8'b11000101 >>> 2 => 8'b00110001
8'b11000101 <<  2 => 8'b00010100
8'b11000101 <<< 2 => 8'b00010100
Run Code Online (Sandbox Code Playgroud)

对于有符号值,只有算术和逻辑左移操作是相同的,但算术右移会导致符号扩展。假设 8'b11000101 是 8 位有符号数 -59 的二进制表示,那么

8'b11000101 >>  2 => 8'b00110001
8'b11000101 >>> 2 => 8'b11110001
8'b11000101 <<  2 => 8'b00010100
8'b11000101 <<< 2 => 8'b00010100
Run Code Online (Sandbox Code Playgroud)

Python只有逻辑移位运算符,没有算术移位运算符。那么如何在Python中实现有符号和无符号值的算术右移呢?

chq*_*lie 10

Python只有逻辑移位运算符,没有算术移位运算符。那么如何在Python中实现有符号和无符号值的算术右移呢?

Python 实际上只有算术移位运算符:

  • 对于正值和负值,左移n与乘以 2 次方完全相同。n
  • 右移n取决于被移动的值是否为负。正值除以 2 的幂并向n0 舍入,而负值的行为就像1在最高有效位一侧延伸无限的位串,这是负数的二进制补码表示形式的副作用。这转化为一个单一的数学等价:将整数右移一个正整数,n计算结果为除以 2 次方n并将结果向负无穷大舍入,Math.floor(value / 2**n)

如果要模拟负值的无符号右移(如 java 和 javascript 中所示),则必须将负值转换为具有您正在考虑的固定位数的正值。右移将给出期望值:

x = -1
x32 = x & 0xffffffff   # convert to 32-bit unsigned value
x >> 8                 # produces -1
x32 >> 8               # produces 0x00ffffff
Run Code Online (Sandbox Code Playgroud)

  • [这个答案](/sf/answers/408318361/)执行`x + 0x100000000`而不是`x &amp; 0xffffffff`。结果是相同的,但后者实际上清除了符号位(并丢弃进位),在我看来,这更直观。 (2认同)

Spe*_*tre -1

不是Python编码员,但我通常会这样解决这个问题:

x = (x>>1)|(x&80);        //  8bit
x = (x>>1)|(x&8000);      // 16bit
x = (x>>1)|(x&80000000);  // 32bit
Run Code Online (Sandbox Code Playgroud)

因此,只需将原始的 MSb 复制x到位移创建的空间中即可。然而,这仅适用于移位 1 位。对于更多你需要做这样的事情:

if (x<0) x = (x>>2)|0xC0;       else x = x>>2; //  8bit
if (x<0) x = (x>>2)|0xC000;     else x = x>>2; // 16bit
if (x<0) x = (x>>2)|0xC0000000; else x = x>>2; // 32bit

if (x<0) x = (x>>3)|0xE0;       else x = x>>3; //  8bit
if (x<0) x = (x>>3)|0xE000;     else x = x>>3; // 16bit
if (x<0) x = (x>>3)|0xE0000000; else x = x>>3; // 32bit

if (x<0) x = (x>>4)|0xF0;       else x = x>>4; //  8bit
if (x<0) x = (x>>4)|0xF000;     else x = x>>4; // 16bit
if (x<0) x = (x>>4)|0xF0000000; else x = x>>4; // 32bit

if (x<0) x = (x>>5)|0xF8;       else x = x>>5; //  8bit
if (x<0) x = (x>>5)|0xF800;     else x = x>>5; // 16bit
if (x<0) x = (x>>5)|0xF8000000; else x = x>>5; // 32bit

...
Run Code Online (Sandbox Code Playgroud)

您可以为蒙版创建 LUT

LUT8[8]=
    {
    0x00, 
    0x80, 
    0xC0, 
    0xE0, 
    0xF0, 
    0xF8, 
    0xFC, 
    0xFE, 
    }

if (x<0) x = (x>>k)|LUT8[k&7]; else x = x>>k; //  8bit
Run Code Online (Sandbox Code Playgroud)

按位移位的掩码k只是包含k来自 MSb 的数字,然后其余的都是二进制的零。

还有更简单的解决方案(以两次否定为代价),如下所示:

if (x<0) x = -((-x)>>k); else x = x>>k;
Run Code Online (Sandbox Code Playgroud)