1. 程式人生 > >比較兩個數a、b的大小,不能使用大於/小於、if、switch,?:等判斷語句

比較兩個數a、b的大小,不能使用大於/小於、if、switch,?:等判斷語句

本文摘自:http://tangyuan1314.iteye.com/blog/1485559

今天面試碰到了這個問題,知道是位操作,不過當時沒想起來,回來查了查總結一下。其實方法很簡單,就是相減後看符號位,將減的結果的符號位移位到末尾,再和1與操作,或者直接和0x80000000與,得出的符號位再右移至末尾,作為陣列的下標。int型佔4位元組,即32位,在-1為補碼的機器上,1代表負數,0代表正數

C程式碼  收藏程式碼
  1. <span style="white-space: normal; background-color: #ffffff;">int max(int x,int y)</span>  
  2. {  
  3. int buf[2]={x,y};  
  4. unsigned int z;  
  5.         z=x-y;  
  6.         z=z>>31;//符號位移至末尾,作為陣列下標  
  7. return buf[z];  
  8. }  

上述程式碼是從網上查的,這是基礎版本,但是未考慮記憶體溢位,如127-(-128)得出的結果超出了8位所能表示的數。並且上述版本不可直接用java寫,java中沒有無符號型別,因為java中右移運算補位是根據符號位,符號位為1就補1,符號位為0就補0,因此右移後可和1與操作。

下面的版本是考慮溢位,即判斷兩個數的符號相同否,相同的話就不會產生溢位。不相同的話,直接看符號位就ok了。下面上程式碼。

C程式碼  收藏程式碼
  1. int max1(int x,int y){//符號相同,返回x,y中的大值  
  2.   unsigned int z;  
  3.   z=((x-y)>>31)&1;  
  4.   return (1-z)*x+z*y;//這種寫法很巧妙  
  5. }  
  6. int max2(int x,int y){//符號不同,直接判斷x的正負即可,返回最大值  
  7.   unsigned int z;  
  8.   z=(x>>31)&1;  
  9.   return (1-z)*x+z*y;  
  10. }  
  11. int max(int x,int y){  
  12.   unsigned int z;  
  13.   z=((x^y)>>31)&1;//異或操作,判斷符號位是否相同
      
  14.   return (1-z)*max1(x,y)+z*max2(x,y);  
  15. }  

 在考慮溢位的情況下,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程式碼  收藏程式碼
  1. {  
  2.         int g, r1, r2;  
  3.         g = ((a^b) >;>; 31) & 1;  
  4.         r1 = (a >;>; 31) & 1;  
  5.         r2 = ((a-b) >;>; 31) & 1;  
  6.         return g*(r1*b + (1-r1)*a) + (1-g)*(r2*b + (1-r2)*a);  
  7. }  
 

 總結一下:

1、基本思想:位操作,找符號位

a^b 的最高位 ---- 判斷符號是否相同

a-b 的最高位 ---- 在符號相同的情況下判斷大小

a   的最高位 ---- a的符號

b   的最高位 ---- b的符號

2、得出大(小)的數

   把大小關係對應到一個數組當中

    使用插值


補充:
以上max1、max2和max的程式碼中可以優化成無需乘法運算,下面以max1為例進行優化說明:

  1. int max1(int x,int y){//符號相同,返回x,y中的大值  
  2.   unsigned int z;  
  3.   z=(x-y)>>31;  //注意:負數右移,高位補1;正數右移,高位補0
  4.   return (~z & x) + (z & y);  
  5. }