今天碰到了負數除法與右移的替代問題,藉機研究一下C語言中負數除法的問題。此處只討論整數除2的冪次方的情況,並取-7~-5除以4爲例,不討論一般除法的移位優化。
1. 負數右移
對於有符號整數,右移時按高位補符號位的原則,則結果會向小於它的整數取整。如
-5 >> 2 = -2;
-6 >> 2 = -2;
-7 >> 2 = -2;
2. 負數除法(截斷)
博文《議“右移一位 等同於 除以二”》http://blog.chinaunix.net/uid-406135-id-3421605.html中有這樣一段描述:“C99和C++0x規定,商向零靠近取整,在 C99 和 C++0x 之前,只保證商*除數+餘數==被除數,如果希望商向零靠近取整,應當使用div函數。”
那上述算式就會有結果:
-5 / 4 = -1;
-6 / 4 = -1;
-7 / 4 = -1;
3. 負數除法(四捨五入)
四捨五入的意義就在於向臨近的整數取整,但問題就在於對.5這個臨界點如何進行處理。這裏有一種說法,即是說對於負數相除四捨五入的情況,可以先將符號拿掉,按正整數做除法,最後再把符號位加到結果上即可。那上述算式的結果應該爲:
-5 / 4 = -1;
-6 / 4 = -2;
-7 / 4 = -2;
即
sign(val) * (abs(val) +(1<< (n-1) )>> n)。
Microsoft Excel的取整就是採用這種方法。
但是,在ARM的NEON運算裏的rounding,以及java的Math.round()函數的返回值來看(參考http://www.cnblogs.com/jiutianhe/archive/2012/10/07/2755655.html):
在.5這個臨界點上,對於正負值都是向上取整。即是說按公式(val+ (1 << (n-1))) >> n進行計算。即:
-5 / 4 = -1;
-6 / 4 = -1;
-7 / 4 = -2;