算術與邏輯位移(arithmetic and logical shifts)
位移把一個數的所有位元朝某個方向滑動若干個位置,就像把算盤上一排珠子往左或往右撥。一端會開出新的空位,另一端的位元則掉出去。位移對硬體來說很便宜,且有個漂亮的算術意義:左移一個位置把一個無號數乘以 2,右移一個位置把它(大致)除以 2——因為每個位元的位值不是加倍就是減半。
左移很簡單:位元往左移,右邊補進 0,0b0011(3)左移 1 變成 0b0110(6)。右移分成兩種口味,這正是關鍵。邏輯右移永遠用 0 填補空出來的高位;這對無號數正確。算術右移則把符號位元複製進高位(與符號擴展同一個想法),使負數保持為負;這對有號數正確。例如 8 位元有號值 -8 是 1111 1000;算術右移 1 得到 1111 1100(-4,正確),而邏輯右移會得到 0111 1100(124,對有號值是錯的)。
位移到處都用得到:快速地乘或除二的次方、抽取或打包位元欄位、建立位址的標籤/索引/位移切分。兩個誠實的提醒:第一,用位移當快速乘除只對二的次方有效,而用位移做有號除法是朝負無窮捨入、不是朝零,所以 -7 右移 1 得到 -4、而非整數除法的 -3——這是個微妙的正確性陷阱。第二,位移量達到或超過字組寬度,在許多語言裡是未定義或不可預測的,所以別假設把 32 位元值移 32 位會得到零。
對 8 位元圖樣 1111 1000 做右移,邏輯與算術之別:邏輯 >> 1 = 0111 1100,但算術 >> 1 = 1111 1100。當成有號讀,1111 1000 是 -8,只有算術位移給出正確的 -4(邏輯位移給出 124)。
右移:邏輯用 0 填補(給無號),算術複製符號位元(給有號)。
用位移當除法是朝負無窮捨入而非朝零:-7 >> 1 得到 -4,不同於整數除法的 -3。此外,位移量達到或超過字組寬度,在許多語言裡是未定義的。