翻看golang
源码的时候看到在本可以进行除 2 的地方,实际实现写的是右移一位,因而有了这里的思考。
那么右移一位和除 2 在不考虑性能的情况下,有什么区别呢?
先看两个示例
1 | println(5 / 2) //2 |
除法表现出来的现象是向 0 取整的。
再来看一下右移:
1 | println(5 >> 1) //2 |
这里就出现了差异,可以看出负奇数的右移是向下取整的。
那这种表象是什么原因呢?这就涉及到负数的二进制表示。
负数在计算机中的表示是其补码的形式
1 | -5的二进制表示,最高位位符号位 |
实际参与运算的也是补码,所以 -5 右移一位结果就是
1 | println(-5 >> 1) |
右移操作完成后,再将补码转会原码就可以发现,结果是 -3。
在这里右移操作后左侧位是补 1 的。
位移操作分为两种:逻辑移位和算数移位。
对于左移来说,这两者是相同的,均是移除左边界的那些位丢失,右边空出来的补 0 。
对于右移,情况则不同,逻辑右移是在左侧补 0 ;而算数右移左侧补入的位取决于数值本身的符号,整数的最高位为 0 (正数),则移入位补 0 ;最高位为 1 (负数),则移入位补 1 。
算数右移具体采用哪种方式取决于编译器的具体实现,可以写一个 demo 程序去测试一下。