本文分為上下兩篇
上文提到了終極三問:為什麼兩個較大數之和為一個較小數?為什麼兩個正數之和為負數?為什麼正數比負數小?
為了解答這三個問題,我們繼續修行内功!繼續研究一下整型數!
1.無符号數和有符号數的截斷在很多應用中,需要将一個較大的數據類型轉換成一個較小的數據類型,這種轉換過程稱為截斷。對于無符号數和有符号數截斷操作會直接将高位丢棄,會改變數據的數值,也會改變數據的位級表示。
截斷示例程序如下:
#include<stdio.h> int main() { unsigned int un_num = 1004; signed int num = -1004; unsigned char un_truncation = (unsigned int ) un_num; signed char truncation = (int)num; printf("truncation = %d , un_truncation = %u , un_num = %x , num = %x \n", truncation , un_truncation ,un_num , num); return 0; }
運行結果如下:
示例分析:無符号數un_num(1004) 的二進制表示為0x3ec ,數據截斷為1字節後的二進制表示為0xec(236)。因此無符号數截斷操作會直接将高位丢棄,會改變數值,也會改變位級表示。有符号數num (-1004)的二進制表示為0xfffffc14 ,數據截斷為1字節後的二進制表示為0x14(20)。因此有符号數截斷操作會直接将高位丢棄,會改變數值,也會改變位級表示。
總結:對于無符号數和有符号數截斷操作會直接将高位丢棄,截斷操作會改變數據的數值,也會改變數據的位級表示。
2.整型數運算整數由無符号數和有符号數組成,整數運算偶爾會出現以下情況:
1、兩個較大數之合為一個較小數。2、兩個正數之合為負數。3、正數比負數小。
我們将深入學習一下整數的運算原理,并一一解答這3個問題。
2.1無符号數加法
假設兩個n位無符号數X和Y滿足:
這兩個數之合的範圍為:
的範圍超出了n位無符号數的範圍,這種情況下将丢棄權重大于n的位。
兩個n位無符号數X和Y加法原理如下:
溢出是指的完整的整數結果不能放到數據類型的字長限制中去,這種情況下将丢棄超出字長限制的位。以4位無符号數加為例:
無符号數加法示例程序如下:
#include<stdio.h> int main() { unsigned int data1 = 4000000000, data2 = 2 , data3 = 294967299; unsigned int sum1 , sum2 ; sum1 = data1 data2; sum2 = data1 data3; printf("sum1 = %u , sum2 = %u \n", sum1 , sum2 ); return 0; }
運行結果如下:
由運行結果可知:
sum1 = 4000000000 2 = 4000000002, 結果正确 。sum2 = 4000000000 294967299= 3 ,結果溢出。sum2 為32位數(0位~31位),丢棄結果超出字長限制的位(第32位),這種運算類似模運算。
這就解釋了終極三問中的:為什麼兩個較大數之和為一個較小數?
無符号數加法溢出往往會造成不可預料的後果,因此我們需要檢查運算是否溢出,無符号數加法溢出檢查原理如下:
假設有兩無符号數X和Y , Z = X Y, 當 Z < X 或者Z < Y時,發生了溢出。
2.2有符号數加法有符号數在計算機中以補碼的形式存在,兩個n位有符号數X和Y加法原理如下:
由公式可知兩個n位有符号數相加時,當X Y值大于等于2的(n-1)次方 時發生了正溢出,此時結果變為一個負數;當 X Y 小于等于負的2的(n-1)次方時發生負溢出,此時的結果變為一個正數。以4位有符号數加為例:
有符号數加法示例程序如下:
#include<stdio.h> int main() { char data1 = 70, data2 = 70 , data3 = -80, data4 = -80; char sum1 , sum2; sum1 = data1 data2; sum2 = data3 data4; printf("sum1 = %d , sum2 = %d \n", sum1 , sum2 ); return 0; }
示例程序運行結果如下:
由實驗結果可知: 70 70的結果為-116, -80 (-80)的結果為96。
運算分析:70的二進制值為0x46 , 70 70 的二進制結果為0X8c (140) , 0X8c表示的補碼值為-116 。由此可見,計算機用兩個二進制數做計算,并不判斷這個數是否是補碼,一視同仁的進行計算,隻是在輸出的時間,根據不同的要求解釋成不同的值。如0X8c可以被解釋成140,也可以解釋成-116。
這就解釋了終極三問中:為什麼兩個正數之和為負數?
2.3有符号數和無符号數比較在很多編程标準和規範中都規定了,有符号數和無符号數不能比較大小,往往會出現負數大于正數。有符号數和無符号數比較大小示例程序如下:
#include<stdio.h> int main() { int data1 = -3 ; unsigned int data2 = 80; printf("data1 = %x ,data2 =%x \n" , data1 ,data2); if(data1 > data2) { printf(" %d big than %d\n", data1 , data2); } else { printf(" %d big than %d\n", data2 , data1); } return 0; }
示例程序運行結果如下:
分析運行結果:在比較大小時計算器認為-3大于80 ,原因是負數是以補碼形式存在,計算機在比較-3和80這兩個數時,直接将這兩個數的二進制值0xfffffffd(-3)和0x00000050(80)進行比較,很顯然計算器認為0xfffffffd更大。
這就解釋了終極三問中:為什麼正數比負數小?
所以我們編程要符合規定,不能用有符号和無符号數直接比較大小。
3.總結綜上所述我們來總結一下:1、位級表示編譯器在編譯程序時,用二進制補碼形式表示負數,用傳統二進制形式表示正數。代碼在機器中運行時,負數以二進制補碼形式存在,正數以傳統二進制形式存在。
例如:負數-3在計算機中以0xfffffffd二進制形式存在,正數80在計算器中以0x00000050二進制形式存在。
2、位級運算計算機在運算時,不判斷對象是否是負數(補碼)還是正數,直接用二進制數去作計算。
例1:-3 80 = 0xfffffffd 0x00000050 = 0x0000004d (高位溢出) = 77例2:-3 比較 80 = 0xfffffffd 比較 0x00000050 = 0xfffffffd > 0x00000050 = -3 >80
3、位級顯示在輸出數據時,計算機會根據用戶設置的類型,将二進制數解釋成相應的值,同一個二進制值,不同的解釋可能有不同的數值。
例如:二進制值為0xfffffffd ,用戶認為這個數是一個有符号數printf 用%d 格式輸出時,計算器會将這個數解釋成-3;當用戶認為這個數是一個有符号數printf 用%u 格式輸出時計算器會将這個數解釋成4294967293 。
創作不易希望朋友們點贊,轉發,評論,關注。您的點贊,轉發,評論,關注将是我持續更新的動力作者:李巍Github:liyinuoman2017CSDN:liyinuo2017今日頭條:程序猿李巍
,