Language agnostic 什么是按位移位(bit shift)运算符,它们是如何工作的?

Language agnostic 什么是按位移位(bit shift)运算符,它们是如何工作的?,language-agnostic,bit-manipulation,operators,bit-shift,binary-operators,Language Agnostic,Bit Manipulation,Operators,Bit Shift,Binary Operators,我一直试图在业余时间学习C,而其他语言(C#,Java等)也有同样的概念(通常是相同的运算符) 我想知道的是,在核心层面上,位移位(,>)有什么作用,它能帮助解决什么问题,以及在拐弯处潜伏着什么陷阱?换言之,这是位移位的绝对初学者指南。假设我们只有一个字节: 0110110 应用单个左位移位可使我们: 1101100 最左边的零被移出字节,新的零被追加到字节的右端 钻头不会翻转;它们被丢弃了。这意味着如果你左移1101100,然后右移它,你不会得到同样的结果回来 向左移动N等于乘以2N 右移

我一直试图在业余时间学习C,而其他语言(C#,Java等)也有同样的概念(通常是相同的运算符)


我想知道的是,在核心层面上,位移位(
>
)有什么作用,它能帮助解决什么问题,以及在拐弯处潜伏着什么陷阱?换言之,这是位移位的绝对初学者指南。

假设我们只有一个字节:

0110110
应用单个左位移位可使我们:

1101100
最左边的零被移出字节,新的零被追加到字节的右端

钻头不会翻转;它们被丢弃了。这意味着如果你左移1101100,然后右移它,你不会得到同样的结果回来

向左移动N等于乘以2N

右移N is(如果使用)相当于除以2N并四舍五入为零

如果您使用的是2的幂,那么位移位可以用于非常快的乘法和除法。几乎所有低级图形例程都使用位移位

例如,在过去,我们在游戏中使用了13h模式(320x200 256色)。在模式13h中,视频内存按像素顺序排列。这意味着要计算像素的位置,可以使用以下数学:

memoryOffset = (row * 320) + column
现在,回到那个时代,速度是至关重要的,所以我们将使用位移位来完成这个操作

然而,320不是二的幂,所以为了解决这个问题,我们必须找出什么是二的幂,加起来就是320:

(row * 320) = (row * 256) + (row * 64)
现在我们可以将其转换为左移位:

(row * 320) = (row << 8) + (row << 6)
总计:在任何有这些计时的古代CPU上都有28个周期

Vrs

在同一个古老的CPU上运行12个周期

是的,我们会努力减少16个CPU周期

在32位或64位模式下,两个版本都会变得更短更快。像Intel Skylake(请参阅)这样的现代无序执行CPU具有非常快的硬件乘法(低延迟和高吞吐量),因此增益要小得多。AMD推土机系列有点慢,尤其是64位乘法器。在Intel CPU和AMD Ryzen上,两次移位的延迟稍低,但指令数比乘法多(这可能导致吞吐量降低):

vs

编译器将为您执行此操作:请参阅如何执行

这里要注意的最有趣的事情是,它可以执行小的左移位,同时执行加法,其性能相当于
add
指令。ARM甚至更强大:任何指令的一个操作数都可以免费左移或右移。因此,通过编译时常量(已知为2的幂)进行缩放比乘法更有效


好吧,回到现代。。。现在更有用的方法是使用位移位在16位整数中存储两个8位值。例如,在C#中:


<>在C++中,编译器使用了两个8位成员的“<代码>结构>代码>,但实际上并不总是这样。

< P>一个原因是以下是依赖于实现的(根据ANSI标准):

x现在可以是127(01111111)或仍然是-1(11111111)


在实践中,通常是后者。

位移位运算符完全按照其名称执行。它们移位位。以下是对不同轮班操作员的简要介绍(或不那么简要)

运营商
  • >
    是算术(或有符号)右移运算符
  • >
    是逻辑(或无符号)右移运算符

  • 位操作,包括位移位,是底层硬件或嵌入式编程的基础。如果您阅读设备规范或某些二进制文件格式,您将看到字节、字和DWORD被分解为非字节对齐的位字段,其中包含各种感兴趣的值。访问这些位字段进行读/写是最常见的用法

    图形编程中的一个简单实例是,16位像素表示如下:

      bit | 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1  | 0 |
          |       Blue        |         Green         |       Red          |
    
    要获得绿色值,请执行以下操作:

     #define GREEN_MASK  0x7E0
     #define GREEN_OFFSET  5
    
     // Read green
     uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;
    
    解释

    为了获得仅绿色的值,即从偏移量5开始到10结束(即6位长),您需要使用(位)掩码,当对整个16位像素应用该掩码时,将仅产生我们感兴趣的位

    #define GREEN_MASK  0x7E0
    
    适当的掩码为0x7E0,二进制为00000 111111 00000(十进制为2016)

    要应用掩码,请使用AND运算符(&)

    应用掩码后,您将得到一个16位的数字,实际上它只是一个11位的数字,因为它的MSB位于第11位。绿色实际上只有6位长,因此我们需要使用右移(11-6=5)将其缩小,因此使用5作为偏移量(
    #define Green#u offset 5

    同样常见的是使用位移位进行2次幂的快速乘法和除法:

     i <<= x;  // i *= 2^x;
     i >>= y;  // i /= 2^y;
    
    i=y;//i/=2^y;
    
    位屏蔽和移位 位移位通常用于低级图形编程。例如,在32位字中编码的给定像素颜色值

     Pixel-Color Value in Hex:    B9B9B900
     Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000
    
    为了更好地理解,使用相同的二进制值标记什么部分代表什么颜色部分

                                     Red     Green     Blue       Alpha
     Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000
    
    比如说,我们想要得到这个像素颜色的绿色值。我们可以通过掩蔽和移位很容易地得到那个值

    我们的面具:

                      Red      Green      Blue      Alpha
     color :        10111001  10111001  10111001  00000000
     green_mask  :  00000000  11111111  00000000  00000000
    
     masked_color = color & green_mask
    
     masked_color:  00000000  10111001  00000000  00000000
    
    逻辑
    &
    运算符确保仅保留掩码为1的值。我们现在要做的最后一件事是,通过将所有这些位右移16位(逻辑右移),得到正确的整数值

    等等,我们有一个整数,表示像素颜色中的绿色数量:

     Pixels-Green Value in Hex:     000000B9
     Pixels-Green Value in Binary:  00000000 00000000 00000000 10111001
     Pixels-Green Value in Decimal: 185
    

    这通常用于编码或解码图像格式,如
    jpg
    png
    ,等等。

    请注意,在Java实现中,要移位的位数是
    00000000 00000000 00000000 00001100
    
    11100000 00000000 00000000 00000000
    
    11000000 00000000 00000000 00000000
    
    00000000 00000000 00000000 00001100
    
    00000000 00000000 00000000 00000110
    
    00111000 00000000 00000000 00000110
    
    10000000 00000000 00000000 01100000
    
    00001000 00000000 00000000 00000110
    
    10000000 00000000 00000000 01100000
    
    11111000 00000000 00000000 00000110
    
      bit | 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1  | 0 |
          |       Blue        |         Green         |       Red          |
    
     #define GREEN_MASK  0x7E0
     #define GREEN_OFFSET  5
    
     // Read green
     uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;
    
    #define GREEN_MASK  0x7E0
    
    uint16_t green = (pixel & GREEN_MASK) ...;
    
    uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;
    
     i <<= x;  // i *= 2^x;
     i >>= y;  // i /= 2^y;
    
     Pixel-Color Value in Hex:    B9B9B900
     Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000
    
                                     Red     Green     Blue       Alpha
     Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000
    
                      Red      Green      Blue      Alpha
     color :        10111001  10111001  10111001  00000000
     green_mask  :  00000000  11111111  00000000  00000000
    
     masked_color = color & green_mask
    
     masked_color:  00000000  10111001  00000000  00000000
    
     green_value = masked_color >>> 16
    
     Pixels-Green Value in Hex:     000000B9
     Pixels-Green Value in Binary:  00000000 00000000 00000000 10111001
     Pixels-Green Value in Decimal: 185
    
    (long) 4 >> 65
    
    (long) 4 >> (65 % 64)
    
    # Basic bit operations
    # Integer to binary
    print(bin(10))
    
    # Binary to integer
    print(int('1010', 2))
    
    # Multiplying x with 2 .... x**2 == x << 1
    print(200 << 1)
    
    # Dividing x with 2 .... x/2 == x >> 1
    print(200 >> 1)
    
    # Modulo x with 2 .... x % 2 == x & 1
    if 20 & 1 == 0:
        print("20 is a even number")
    
    # Check if n is power of 2: check !(n & (n-1))
    print(not(33 & (33-1)))
    
    # Getting xth bit of n: (n >> x) & 1
    print((10 >> 2) & 1) # Bin of 10 == 1010 and second bit is 0
    
    # Toggle nth bit of x : x^(1 << n)
    # take bin(10) == 1010 and toggling second bit in bin(10) we get 1110 === bin(14)
    print(10^(1 << 2))
    
    Operator     Usage
    
     <<           Indicates the bits are to be shifted to the left.
    
     >>           Indicates the bits are to be shifted to the right.
    
    0000111110110011
    
    0111110110011000
    
    0000000111110110