上一篇文章我們嘮叨了浮點數,知道了浮點數存儲小數是不精確的。本篇繼續嘮叨一下MySQL中的另一種存儲小數的方式 —— 定點數。浮點數文章閃現:
什么, 0.3 - 0.2 ≠ 0.1 ? 什么鬼
定點數類型
正因為用浮點數表示小數可能會有不精確的情況,在一些情況下我們必須保證小數是精確的,所以設計MySQL
的大叔們提出一種稱之為定點數
的數據類型,它也是存儲小數的一種方式:
其中:
M
表示該小數最多需要的十進制有效數字個數。注意是
有效數字
個數,比方說對于小數-2.3
來說有效數字個數就是2,對于小數0.9
來說有效數字個數就是1
。D
表示該小數的小數點后的十進制數字個數。這個好理解,小數點后有幾個十進制數字,
D
的值就是什么。
舉個例子看一下,設置了M
和D
的單精度浮點數的取值范圍的變化:
類型 | 取值范圍 |
---|---|
DECIMAL(4, 1) | -999.9~999.9 |
DECIMAL(5, 1) | -9999.9~9999.9 |
DECIMAL(6, 1) | -99999.9~99999.9 |
DECIMAL(4, 0) | -9999~9999 |
DECIMAL(4, 1) | -999.9~999.9 |
DECIMAL(4, 2) | -99.99~99.99 |
可以看到,在D相同的情況下,M越大,該類型的取值范圍越大;在M相同的情況下,D越大,該類型的取值范圍越小。當然,M
和D
的取值也不是無限大的,M
的取值范圍是1~255
,D
的取值范圍是0~30
,而且D
的值必須不大于M
。M
和D
都是可選的,如果我們省略了它們,那它們的值按照機器支持的最大值來存儲。
我們說定點數是一種精確的小數,為了達到精確的目的我們就不能把它轉換成二進制小數之后再存儲(因為有很多十進制小數轉為二進制小數后需要進行舍入操作,導致二進制小數表示的數值是不精確的)。其實轉念一想,所謂的小數只是把兩個十進制整數用小數點分割開來而已,我們只要把小數點左右的兩個十進制整數給存儲起來,那不就是精確的了么。比方說對于十進制小數2.38
來說,我們可以把這個小數的小數點左右的兩個整數,也就是2
和38
分別保存起來,那么不就相當于保存了一個精確的小數么,這波操作是不是很6。
當然事情并沒有這么簡單,對于給定M
、D
值的DECIMAL(M, D)
類型,比如DEMCIMAL(16, 4)
來說:
首先確定小數點左邊的整數最多需要存儲的十進制位數是12位,小數點右邊的整數需要存儲的十進制位數是4位,如圖所示:
從小數點位置出發,每個整數每隔9個十進制位劃分為1組,效果就是這樣:
從圖中可以看出,如果不足9個十進制位,也會被劃分成一組。
針對每個組中的十進制數字,將其轉換為二進制數字進行存儲,根據組中包含的十進制數字位數不同,所需的存儲空間大小也不同,具體見下表:
組中包含的十進制位數 占用存儲空間大小(單位:字節) 1或2 1 3或4 2 5或6 3 7或8或9 4 所以
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
的值不能超過M
。