1. 程式人生 > >利用位運算計算某種資料型別的最大值和最小值

利用位運算計算某種資料型別的最大值和最小值

  1. 常見數值的補碼
     數值
    補碼
     0
    0000 0000
     1
    0000 0001
      -1
    1111 1111
    -256
    1000 0000
      255
    0111 1111

    最高位是符號位,0表示正數,1表示負數。

  2. 已知負數求補碼
    方法:將對應絕對值的補碼按位取反,然後加上1,比如求-1的補碼,先取1的補碼:
    0000 0001 1
    按位取反 1111 1110
    加上1 1111 1111 -1
  3. 已知負數的補碼,求負數
    方法:補碼-1,然後按位取反得到絕對值,然後加上負號
    1111 1111
    減去1 1111 1110
    按位取反 0000 0001 1
    加上負號 -1
    也可以直接按位取反,得到絕對值加上1,然後加上負號
    1111 1111
    按位取反 0000 0000 0
    加上1 1
    填上負號 -1
    再如:
    1000 0000
    按位取反 0111 1111 127
    加上1 128
    填上負號 -128
  4. 最大值和最小值的特徵
    255 0111 1111
    -256 1000 0000
    32768 0111 1111 1111 1111
    -32769 1000 0000 0000 0000
    從以上補碼,可以看出,任何一種資料型別,其最大值的特點是最高位為0,其餘為均為1,而最小值,其特點是最高位為1,其餘位均為0。因此對於任何一種資料型別,只要通過位移運算,得到符合上述特點的補碼就可以獲取到相應資料型別的最大值和最小值。現假設要計算短整型的資料範圍,則其最大值和最小值的補碼如下:
    最大值 0111 1111 1111 1111
    最小值 1000 0000 0000 0000
    要獲得以上補碼,我們可以看到,最簡單的是獲得最小值的補碼,它可以通過對1左移15位獲得,而如果獲得了最小值的補碼,那麼按位取反,就可以獲得最大值的補碼,過程如下:
    0000 0000 0000 0001  
    左移15位 1000 0000 0000 0000 -32769
    按位取反 0111 1111 1111 1111 32768

    下面是實驗:
    1<<(sizeof(int)*8-1)
    -2147483648
    ~(1<<(sizeof(int)*8-1))
    2147483647
    
    注意以下差異:
    (short)(1<<(sizeof(short)*8-1))
    -32768
    (short)(~(1<<(sizeof(short)*8-1)))
    32767
    ((short)1)<<(sizeof(short)*8-1)
    32768
    ~(((short)1)<<(sizeof(short)*8-1))
    -32769
    
    ((int)1)<<(sizeof(int)*8-1)
    -2147483648
    ~(((int)1)<<(sizeof(int)*8-1))
    2147483647
    
    ((long)1)<<(sizeof(long)*8-1)
    -2147483648
    ~(((long)1)<<(sizeof(long)*8-1))
    2147483647

    於是,對於一個對輸入的整形數乘以一定倍數的模板函式,可以通過下面的方式確定數值範圍:
    template<typename T>
    int Test(T x,int nScale)
    {
    	T nMin = (T)(1<<(sizeof(x)*8-1));		//確定該型別的最大值
    	T nMax = (T)(~(1<<(sizeof(x)*8-1)));		//確定該型別的最小值
    	int nVal = x*nScale;			//乘以一定的倍數
    	if(nVal<nMin) return nMin;		//如果越下界,則返回下界值
    	else if(nVal>nMax) return nMax;		//如果越上界,則返回上界值
    	else return nVal;			//沒有越界,返回乘倍後的值
    }