謂定點小數,就是小數點的位置是固定的。咱們是要用整數來表示定點小數,因爲小數點的位置是固定的,因此就沒有必要儲存它(若是儲存了小數點的位置,那就是浮點數了)。既然沒有儲存小數點的位置,那麼計算機固然就不知道小數點的位置,因此這個小數點的位置是咱們寫程序的人本身須要牢記的。html
先以10進製爲例。若是咱們可以計算12+34=46的話,固然也就可以計算1.2+3.4 或者 0.12+0.34了。因此定點小數的加減法和整數的相同,而且和小數點的位置無關。乘法就不一樣了。 12*34=408,而1.2*3.4=4.08。這裏1.2的小數點在第1位以前,而4.08的小數點在第2位以前,小數點發生了移動。因此在作乘法的時候,須要對小數點的位置進行調整?!但是既然咱們是作定點小數運算,那就說小數點的位置不能動!!怎麼解決這個矛盾呢,那就是捨棄最低位。 也就說1.2*3.4=4.1,這樣咱們就獲得正確的定點運算的結果了。因此在作定點小數運算的時候不只須要牢記小數點的位置,還須要記住表達定點小數的有效位數。上面這個例子中,有效位數爲2,小數點以後有一位。算法
如今進入二進制。咱們的定點小數用16位二進制表達,最高位是符號位,那麼有效位就是15位。小數點以後能夠有0 - 15位。咱們把小數點以後有n位叫作Qn,例如小數點以後有12位叫作Q12格式的定點小數,而Q0就是咱們所說的整數。post
Q12的正數的最大值是 0 111 . 111111111111,第一個0是符號位,後面的數都是1,那麼這個數是十進制的多少呢,很好運算,就是 0x7fff / 2^12 = 7.999755859375。對於Qn格式的定點小數的表達的數值就它的整數值除以2^n。在計算機中仍是以整數來運算,咱們把它想象成實際所表達的值的時候,進行這個運算。url
反過來把一個實際所要表達的值x轉換Qn型的定點小數的時候,就是x*2^n了。例如 0.2的Q12型定點小數爲:0.2*2^12 = 819.2,因爲這個數要用整數儲存, 因此是819 即 0x0333。由於捨棄了小數部分,因此0x0333不是精確的0.2,實際上它是819/2^12 =0.199951171875。htm
咱們用數學表達式作一下總結:
x表示實際的數(*一個浮點數), q表示它的Qn型定點小數(一個整數)。
q = (int) (x * 2^n)
x = (float)q/2^nblog
由以上公式咱們能夠很快得出定點小數的+-*/算法:
假設q1,q2,q3表達的值分別爲x1,x2,x3
q3 = q1 + q2 若 x3 = x1 + x2
q3 = q1 - q2 若 x3 = x1 - x2
q3 = q1 * q2 / 2^n若 x3 = x1 * x2
q3 = q1 * 2^n / q2若 x3 = x1 / x2
咱們看到加減法和通常的整數運算相同,而乘除法的時候,爲了使得結果的小數點位不移動,對數值進行了移動。
用c語言來寫定點小數的乘法就是:
short q1,q2,q3;
....
q3=((long q1) * (long q2)) >> n;
因爲/ 2^n和* 2^n能夠簡單的用移位來計算,因此定點小數的運算比浮點小數要快得多。下面咱們用一個例子來驗證一下上面的公式:
用Q12來計算2.1 * 2.2,先把2.1 2.2轉換爲Q12定點小數:
2.1 * 2^12 = 8601.6 = 8602
2.2 * 2^12 = 9011.2 = 9011
(8602 * 9011) >> 12 = 18923
18923的實際值是18923/2^12 = 4.619873046875 和實際的結果 4.62相差0.000126953125,對於通常的計算已經足夠精確了。get