你可能不知道的MySQL中的定點數型別
定點數型別
正因為用浮點數表示小數可能會有不精確的情況,在一些情況下我們必須保證小數是精確的,所以設計MySQL的大叔們提出一種稱之為定點數的資料型別,它也是儲存小數的一種方式:
其中:
- M表示該小數最多需要的十進位制有效數字個數。
注意是有效數字個數,比方說對於小數**-2.3來說有效數字個數就是2,對於小數0.9來說有效數字個數就是1**。
- D表示該小數的小數點後的十進位制數字個數。
這個好理解,小數點後有幾個十進位制數字,D的值就是什麼。
舉個例子看一下,設定了M和D的單精度浮點數的取值範圍的變化:
可以看到,在D相同的情況下,M越大,該型別的取值範圍越大;在M相同的情況下,D越大,該型別的取值範圍越小
我們說定點數是一種精確的小數,為了達到精確的目的我們就不能把它轉換成二進位制小數之後再儲存(因為有很多十進位制小數轉為二進位制小數後需要進行舍入操作,導致二進位制小數表示的數值是不精確的)。其實轉念一想,所謂的小數只是把兩個十進位制整數用小數點分割開來而已,我們只要把小數點左右的兩個十進位制整數給儲存起來,那不就是精確的了麼。比方說對於十進位制小數2.38來說,我們可以把這個小數的小數點左右的兩個整數,也就是2和38
當然事情並沒有這麼簡單,對於給定M、D值的**DECIMAL(M, D)型別,比如DEMCIMAL(16, 4)**來說:
- 首先確定小數點左邊的整數最多需要儲存的十進位制位數是12位,小數點右邊的整數需要儲存的十進位制位數是4位,如圖所示:
- 從小數點位置出發,每個整數每隔9個十進位制位劃分為1組,效果就是這樣:
從圖中可以看出,如果不足9個十進位制位,也會被劃分成一組。
- 針對每個組中的十進位制數字,將其轉換為二進位制數字進行儲存,根據組中包含的十進位制數字位數不同,所需的儲存空間大小也不同,具體見下表:
所以DECIMAL(16, 4)共需要佔用8個位元組的儲存空間大小,這8個位元組由下邊3個部分組成:
- 第1組包含3個十進位制位,需要使用2個位元組儲存。
- 第2組包含9個十進位制位,需要使用4個位元組儲存。
- 第3組包含4個十進位制位,需要使用2個位元組儲存。
- 將轉換完成的位元位序列的最高位設定為1。
這些步驟看的有一丟丟懵逼吧,彆著急,舉個例子就都清楚了。比方說我們使用定點數型別DECIMAL(16, 4)來儲存十進位制小數1234567890.1234,這個小數會被劃分成3個部分:
1 234567890 1234
也就是:
- 第1組中包含整數1。
- 第2組中包含整數234567890。
- 第3組中包含整數1234。
然後將每一組中的十進位制數字轉換成對應的二進位制數字:
- 第1組佔用2個位元組,整數1對應的二進位制數就是(位元組之間實際上沒有空格,只不過為了大家理解上的方便我們加了一個空格):
00000000 00000001
二進位制看起來太難受,我們還是轉換成對應的十六進位制看一下:
0x0001
- 第2組佔用4個位元組,整數234567890對應的十六進位制數就是:
0x0DFB38D2
- 第3組佔用2個位元組,整數1234對應的十六進位制數就是:
0x04D2
所以將這些十六進位制數字連起來之後就是:
0x00010DFB38D204D2
最後還要將這個結果的最高位設定為1,所以最終十進位制小數1234567890.1234使用定點數型別**DECIMAL(16, 4)**儲存時共佔用8個位元組,具體內容為:
0x80010DFB38D204D2
有的朋友會問,如果我們想使用定點數型別DECIMAL(16, 4)儲存一個負數怎麼辦,比方說-1234567890.1234,這時只需要將0x80010DFB38D204D2中的每一個位元位都執行一個取反操作就好,也就是得到下邊這個結果:
0x7FFEF204C72DFB2D
從上邊的敘述中我們可以知道,對於DECIMAL(M, D)型別來說,給定的M和D的值不同,所需的儲存空間大小也不同。可以看到,與浮點數相比,定點數需要更多的空間來儲存資料,所以如果不是在某些需要儲存精確小數的場景下,一般的小數用浮點數表示就足夠了。
對於定點數型別DECIMAL(M, D)來說,M和D都是可選的,預設的M的值是10,預設的D的值是0,也就是說下列等式是成立的:
DECIMAL = DECIMAL(10) = DECIMAL(10, 0)
DECIMAL(n) = DECIMAL(n, 0)
另外M的範圍是1~65,D的範圍是0~30,且D的值不能超過