原本只打算理解JS中0.1 + 0.2 == 0.30000000000000004的緣由,但發現本身對計算機的數字表示和運算十分陌生,因而只好惡補一下。
本篇咱們一塊兒來探討一下基礎的基礎——無符號整數的表示方式和加減乘除運算。html
無符號整數只能表示大於或等於零的整數值。其二進制編碼方式十分直觀,僅包含真值域。
咱們以8bit的存儲空間爲例,真值域則佔8bit,所以可表示的數值範圍是{0,...,255},對應的二進制編碼是{00000000,...,11111111}。
從集合論的角度描述,咱們能夠將十進制表示的數值範圍定義爲集合A,將二進制表示的數值範圍定義爲集合B,他們之間的映射爲f。f(a)=b,其中a屬於A、b屬於B。而且f爲雙射函數。所以無符號整數表示方式具備以下特色:ide
可表示的數值範圍小;函數
十進制表示的數值範圍與二進制表示的數值範圍的元素是一一對應的,二者可精確映射轉換。(相對浮點數而言,某些二進制表示的數值只能映射爲十進制表示的數值的近似值而已)編碼
零擴展運算用於在保持數值不變的前提下,不一樣字長的整數之間的轉換。
例如如今咱們要將8bit的00000100擴展爲16bit,那麼咱們只要將高8bit設置爲0即獲得000000000000000100,而其數值並不產生變化。code
截斷會減小位數,並對原始值取模。模爲2^n,n爲截斷後的位數。
例如如今將16bit的000000100000000100截斷爲8bit,那麼結果爲00000100,而模是2^8。htm
注意:位級運算均是模數運算,即加減乘除後均會對運算結果取模,並以取模後的結果做爲終止返回。
無符號整數加法的運算順序:blog
算術加法;ip
執行截斷操做。rem
示例,兩個4bit的無符號數相加(11+6):get
1011 +0110 10001,而後執行截斷獲得0001
無符號整數減法的運算順序:
將減法轉換爲加法(對減數取補碼);
算術加法;
執行截斷操做。
示例,兩個4bit的無符號數相減(11-6):
1011 -0110
對減數求補碼後,減法轉換爲加法
1011 +1010 10101,而後執行截斷獲得0101
對於乘法實質上就是經過移位操做和加、減法組合而成,且根據乘數是否爲2的n次冪區別處理。
對於乘數爲2的n次冪的狀況,乘法公式爲:a<<n,如64等價於6(2^2),則可轉換爲移位操做6<<2便可。而後再對結果取模。
對於乘數不爲2的n次冪的狀況
2.1. 將乘數以二進制形式表示,並以連續的1做爲分組。如43的二進制形式爲00(1)0(1)0(11),從左至右可分紅3組分別是(1)、(1)和(11)。
2.2. 以n表示每組的最高位的指數,以m表示每組最低位的指數。如第一組n=m=5,第二組n=m=3,第三組n=1而m=0。
2.3. 根據公式(x<<n+1)-(x<<m)對每組進行運算,並將結果相加。如(假設被乘數爲2)
第一組:2<<(5+1) - 2<<5 = 64 第二組:2<<(3+1) - 2<<3 = 16 第三組:2<<(1+1) - 2<<0 = 6 相加獲得86
2.4. 對結果取模。
對於除法實質上就是經過移位操做和加、減法組合而成,且根據除數是否爲2的n次冪區別處理。
對於被除數爲2的n次冪的狀況,除法公式爲:a>>n,如6/4等價於6/(2^2),則可轉換爲移位操做6>>2便可。而後再對結果取模。
對於被除數不爲2的n次冪的狀況,則狀況複雜很多。運算步驟以下:(實質上咱們就是按這個步驟作十進制除法的)
2.1. 高位對齊,在除數值小於被除數值的前提下,讓除數的位數等於被除數;若執行高位對齊後,除數值大於被除數時,則除數右移一位。獲得位移數。
2.2. 試商,除數-被除數N = 餘數中間值 ,其中N被除數 <= 除數 && (N+1)被除數 > 除數。商 = 商 + N 基數^位移數。
2.3. 循環執行上述步驟,直到無需再執行高位對齊,那麼2.2中獲得的餘數中間值將做爲除法運算的最終餘數,不然餘數中間值則做爲一下輪高位對齊的被除數處理。
如下是C的實現:
#include <stdio.h> // 前置條件 const unsigned short lowest_bit_weight = 1; // 二進制最低位的位權重 int main(){ // 輸入 unsigned short dividend = 14, divisor = 5; // 輸出 unsigned short quotients = 0, // 商 rem = 0; // 餘數 // 中間值 unsigned short highest_bit_weight, divisor_aligned, tmp_dividend = dividend; unsigned short high_alignment; // 開始運算 while (1){ // 高位對齊 (從高位開始運算) // 結果:1. 要麼被除數的最高位小於除數的最高位; // 2. 要麼被除數的最高位對齊除數的最高位, 且被除數大於除數; high_alignment = 0; highest_bit_weight = lowest_bit_weight; divisor_aligned = divisor; while (tmp_dividend >= divisor_aligned){ divisor_aligned = divisor_aligned << 1; highest_bit_weight = highest_bit_weight << 1; high_alignment += 1; } if (high_alignment > 0){ divisor_aligned = divisor_aligned >> 1; highest_bit_weight = highest_bit_weight >> 1; high_alignment -= 1; } // 當無需執行高位對齊時,則將下一輪的被除數做爲餘數,而且結束運算 if (0 == high_alignment) { rem = tmp_dividend; break; } // 上一輪運算的商加上最高位權重獲得當前運算的商值 quotients = quotients | highest_bit_weight; // 被除數減除數的差值做下一輪的被除數 tmp_dividend = tmp_dividend - divisor_aligned; } printf("%u/%u=%u(rem:%u)\n", dividend, divisor, quotients, rem); return 0; }
尊重原創,轉載請註明來自:http://www.cnblogs.com/fsjohnhuang/p/5078290.html 肥子John^_^
《深刻理解計算機系統》