比較兩個數a、b的大小,不能使用大於/小於、if、switch,?:等判斷語句
本文摘自:http://tangyuan1314.iteye.com/blog/1485559
今天面試碰到了這個問題,知道是位操作,不過當時沒想起來,回來查了查總結一下。其實方法很簡單,就是相減後看符號位,將減的結果的符號位移位到末尾,再和1與操作,或者直接和0x80000000與,得出的符號位再右移至末尾,作為陣列的下標。int型佔4位元組,即32位,在-1為補碼的機器上,1代表負數,0代表正數
C程式碼- <span style="white-space: normal; background-color: #ffffff;">int max(int x,int y)</span>
- {
- int buf[2]={x,y};
- unsigned int z;
- z=x-y;
- z=z>>31;//符號位移至末尾,作為陣列下標
- return buf[z];
- }
上述程式碼是從網上查的,這是基礎版本,但是未考慮記憶體溢位,如127-(-128)得出的結果超出了8位所能表示的數。並且上述版本不可直接用java寫,java中沒有無符號型別,因為java中右移運算補位是根據符號位,符號位為1就補1,符號位為0就補0,因此右移後可和1與操作。
下面的版本是考慮溢位,即判斷兩個數的符號相同否,相同的話就不會產生溢位。不相同的話,直接看符號位就ok了。下面上程式碼。
- int max1(int x,int y){//符號相同,返回x,y中的大值
- unsigned int z;
- z=((x-y)>>31)&1;
- return (1-z)*x+z*y;//這種寫法很巧妙
- }
- int max2(int x,int y){//符號不同,直接判斷x的正負即可,返回最大值
- unsigned int z;
- z=(x>>31)&1;
- return (1-z)*x+z*y;
- }
- int max(int x,int y){
- unsigned int z;
- z=((x^y)>>31)&1;//異或操作,判斷符號位是否相同
- return (1-z)*max1(x,y)+z*max2(x,y);
- }
在考慮溢位的情況下,unsigned int z=((x^y)>;>;31)&1的值有兩種可能,x、y同號時為0,x、y異號時為1。當x、y同號時x-y不會溢位,可參考max1,得出最大值;當x、y異號時,取正的那個就是最大值max。
其實上述的巧妙部分是基於插值,有人給出了精簡的程式,思想同上。int max (int a, int b)
C程式碼- {
- int g, r1, r2;
- g = ((a^b) >;>; 31) & 1;
- r1 = (a >;>; 31) & 1;
- r2 = ((a-b) >;>; 31) & 1;
- return g*(r1*b + (1-r1)*a) + (1-g)*(r2*b + (1-r2)*a);
- }
總結一下:
1、基本思想:位操作,找符號位
a^b 的最高位 ---- 判斷符號是否相同
a-b 的最高位 ---- 在符號相同的情況下判斷大小
a 的最高位 ---- a的符號
b 的最高位 ---- b的符號
2、得出大(小)的數
把大小關係對應到一個數組當中
使用插值
補充:
以上max1、max2和max的程式碼中可以優化成無需乘法運算,下面以max1為例進行優化說明:
- int max1(int x,int y){//符號相同,返回x,y中的大值
- unsigned int z;
- z=(x-y)>>31; //注意:負數右移,高位補1;正數右移,高位補0
- return (~z & x) + (z & y);
- }