Java 算术右位移位存在哪些实际用例?

Java 算术右位移位存在哪些实际用例?,java,c++,bit-manipulation,bit-shift,sign-extension,Java,C++,Bit Manipulation,Bit Shift,Sign Extension,我偶然发现一个问题,问我是否。我在许多项目中广泛使用了位移位,但是,我从来没有使用过算术位移位,即左操作数可以是负数,符号位应该移位而不是零。例如,在Java中,您可以使用>运算符执行算术位移位(而>将执行逻辑移位)。经过深思熟虑,我得出结论,我从来没有使用过左操作数可能为负数的> P>算术移位中甚至定义为C++中的实现,因此与java相比,C++中甚至没有一个标准化的操作符来执行算术移位。答案还指出了一个有趣的问题,我甚至没有意识到负数移位: +63 >> 1 = +31 (in

我偶然发现一个问题,问我是否。我在许多项目中广泛使用了位移位,但是,我从来没有使用过算术位移位,即左操作数可以是负数,符号位应该移位而不是零。例如,在Java中,您可以使用
>
运算符执行算术位移位(而
>
将执行逻辑移位)。经过深思熟虑,我得出结论,我从来没有使用过左操作数可能为负数的
>

<> P>算术移位中甚至定义为C++中的实现,因此与java相比,C++中甚至没有一个标准化的操作符来执行算术移位。答案还指出了一个有趣的问题,我甚至没有意识到负数移位:

+63 >> 1 = +31 (integral part of quotient E1/2E2)
00111111 >> 1 = 00011111
-63 >> 1 = -32 
11000001 >> 1 = 11100000
因此
-63>>1
产生
-32
,这在查看位时很明显,但可能不是大多数程序员第一眼看到的结果。更令人惊讶的是(但在查看位时再次明显)是
-1>>1
-1
,而不是
0


那么,可能负值的算术右移的具体用例是什么呢?

我以前在创建掩码时很方便,然后在操作位字段时在“&”或“|”运算符中使用掩码,用于位数据打包或位图形


我没有一个方便的代码示例,但我确实记得很多年前在黑白图形中使用该技术进行放大(通过扩展一点,1或0)。对于3倍变焦,“0”将变为“000”,而“1”将变为“111”,而无需知道位的初始值。要扩展的位将被置于高阶位置,然后算术右移将扩展它,无论它是0还是1。逻辑移位(无论是左移位还是右移位)总是引入零来填充空位位置。在这种情况下,符号位是解决方案的关键。

我不太清楚您的意思。但是我想推测一下,你想把移位作为一个算术函数。 我看到的一件有趣的事情是二进制数的这个性质

int n = 4;
int k = 1;

n = n << k; // is the same as n = n * 2^k
//now n = (4 * 2) i.e. 8
n = n >> k; // is the same as n = n / 2^k
//now n = (8 / 2) i.e. 4
int n=4;
int k=1;
n=n>k;//与n=n/2^k相同
//现在n=(8/2)即4
希望有帮助


但是是的,你要小心负数
在C语言中,当编写设备驱动程序时,我会屏蔽它,然后相应地将其返回,位移位运算符被广泛使用,因为位被用作需要打开和关闭的开关。位移位使我们能够轻松、正确地瞄准正确的开关

许多哈希和加密函数都使用位移位。看一看


最后,使用位字段来包含状态信息有时很有用。包括位移位在内的位操作函数对这些功能非常有用。

也许最著名的是无分支绝对值:

它使用算术移位将符号位复制到所有位。我遇到的算术移位的大多数用法都是这种形式的。当然,这不需要算术移位,您可以将所有出现的
x>>31
(其中
x
int
)替换为
-(x>>31)


值31来自
int
的大小(以位为单位),在Java中定义为32。因此,右移31会将除符号位之外的所有位移出,符号位(因为它是一个算术移位)被复制到这31位,在每个位置留下符号位的副本。

下面是一个函数示例,该函数将查找大于或等于输入的最小二次方。对于这个问题,还有其他可能更快的解决方案,但不包括任何面向硬件的解决方案,也不只是一系列右移和or。此解决方案使用算术移位来执行二进制搜索

unsigned ClosestPowerOfTwo(unsigned num) {
  int mask = 0xFFFF0000;
  mask = (num & mask) ? (mask << 8) :  (mask >> 8);
  mask = (num & mask) ? (mask << 4) :  (mask >> 4);
  mask = (num & mask) ? (mask << 2) :  (mask >> 2);
  mask = (num & mask) ? (mask << 1) :  (mask >> 1);
  mask = (num & mask) ?  mask       :  (mask >> 1);
  return (num & mask) ? -mask       : -(mask << 1);
}
unsigned ClosestPowerOfTwo(unsigned num){
int mask=0xFFFF0000;
掩码=(数量和掩码)?(掩码>8);
掩码=(数量和掩码)?(掩码>4);
掩码=(数量和掩码)?(掩码>2);
掩码=(数量和掩码)?(掩码>1);
掩码=(数量和掩码)?掩码:(掩码>>1);

return(num&mask)?-mask:-(mask实际上,逻辑右移更常用。但是,有许多操作需要算术移位(或者用算术移位更优雅地解决)

  • 标志扩展名:

    • 大多数情况下,您只处理C中可用的类型,当将较窄的类型强制转换/升级为较宽的类型(如短到int)时,编译器会自动进行符号扩展,因此您可能不会注意到它,但如果体系结构没有符号扩展指令,则会隐藏在a中。对于“奇数”位数您必须手动执行符号扩展,因此这将更加常见。例如,如果将10位像素或ADC值读入16位寄存器的顶部位:
      value>>6
      将位移到较低的10位位置,并进行符号扩展以保留值。如果将它们读入具有顶部的低10位6位为零您将使用
      value>6
      对值进行符号扩展以使用它
    • 使用有符号位字段时,还需要有符号扩展
      struct bitfield {
          int x: 15;
          int y: 12;
          int z: 5;
      };
      
      int f(bitfield b) {
          return (b.x/8 + b.y/5) * b.z;
      }
      
      。移位由编译器生成,但通常不使用位字段(因为它们不可移植),而是对原始整数值进行操作,因此您需要自己进行算术移位来提取字段
    • 另一个示例:符号扩展指针以生成一个。这用于:
      char*指针=(char*)((intptr\u t)address>16)
      。您可以将其视为底部的48位字段
    • V8引擎将值存储在前31位,因此需要右移以恢复有符号整数
  • 例如,
    x/12
    struct bitfield {
        int x: 15;
        int y: 12;
        int z: 5;
    };
    
    int f(bitfield b) {
        return (b.x/8 + b.y/5) * b.z;
    }
    
    uint32_t lsh_saturated(uint32_t x, int32_t n) // returns 0 if n == 32
    {
        return (x << (n & 0x1F)) & ((n-32) >> 5);
    }
    
    uint32_t lsh(uint32_t x, int32_t n) // returns 0 if n >= 32
    {
        return (x << (n & 0x1F)) & ((n-32) >> 31);
    }
    
    min = y + ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1)));
    max = x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1)));