【云圖說】第235期 DDS讀寫兩步走 帶您領略只讀節點的風采
1043
2025-03-31
浮點類型
1. 類型介紹
2. 數據精度說明
3. 精度誤差說明
浮點類型
1. 類型介紹
2. 數據精度說明
3. 精度誤差說明
浮點類型
1. 類型介紹
浮點數和定點數類型的特點是可以處理小數,你可以把整數看成小數的一個特例。因此,浮點數和定點數的使用場景,比整數大多了。 MySQL支持的浮點數類型,分別是 FLOAT、DOUBLE、REAL。
FLOAT 表示單精度浮點數;
DOUBLE 表示雙精度浮點數;
REAL默認就是 DOUBLE。如果把 SQL 模式設定為啟用“REAL_AS_FLOAT”,那 么,MySQL 就認為 REAL 是 FLOAT。如果要啟用“REAL_AS_FLOAT”,可以通過以下 SQL 語句實現:
SET sql_mode = “REAL_AS_FLOAT”;
問題1: FLOAT 和 DOUBLE 這兩種數據類型的區別是什么?
FLOAT 占用字節數少,取值范圍小;DOUBLE 占用字節數多,取值范圍也大。
問題2: 為什么浮點數類型的無符號數取值范圍,只相當于有符號數取值范圍的一半,也就是只相當于有符號數取值范圍大于等于零的部分呢?
MySQL 存儲浮點數的格式為:符號(S)、尾數(M)和 階碼(E)。因此,無論有沒有符號,MySQL 的浮點數都會存儲表示符號的部分。因此, 所謂的無符號數取值范圍,其實就是有符號數取值范圍大于等于零的部分。
2. 數據精度說明
對于浮點類型,在MySQL中單精度值使用4個字節,雙精度值使用8個字節。
MySQL允許使用非標準語法(其他數據庫未必支持,因此如果涉及到數據遷移,則最好不要這么用):FLOAT(M,D)或DOUBLE(M,D)。這里,M稱為精度,D稱為標度。(M,D)中 M=整數位+小數位,D=小數位。 D<=M<=255,0<=D<=30。
例如,定義為FLOAT(5,2)的一個列可以顯示為-999.99-999.99。如果超過這個范圍會報錯。
FLOAT和DOUBLE類型在不指定(M,D)時,默認會按照實際的精度(由實際的硬件和操作系統決定)來顯示。
說明:浮點類型,也可以加UNSIGNED,但是不會改變數據范圍,例如:FLOAT(3,2) UNSIGNED仍然只能表示0-9.99的范圍。
不管是否顯式設置了精度(M,D),這里MySQL的處理方案如下:
如果存儲時,整數部分超出了范圍,MySQL就會報錯,不允許存這樣的值
如果存儲時,小數點部分若超出范圍,就分以下情況:
若四舍五入后,整數部分沒有超出范圍,則只警告,但能成功操作并四舍五入刪除多余的小數位后保存。例如在FLOAT(5,2)列內插入999.009,近似結果是999.01。
若四舍五入后,整數部分超出范圍,則MySQL報錯,并拒絕處理。如FLOAT(5,2)列內插入999.995和-999.995都會報錯。
從MySQL 8.0.17開始,FLOAT(M,D) 和DOUBLE(M,D)用法在官方文檔中已經明確不推薦使用,將來可能被移除。另外,關于浮點型FLOAT和DOUBLE的UNSIGNED也不推薦使用了,將來也可能被移除。
舉例
CREATE TABLE test_double1( f1 FLOAT, f2 FLOAT(5,2), f3 DOUBLE, f4 DOUBLE(5,2) ); DESC test_double1;
INSERT INTO test_double1(f1, f2) VALUES(123.45,123.45); SELECT * FROM test_double1;
存在四舍五入問題
INSERT INTO test_double1(f3,f4) VALUES(123.45,123.456); #存在四舍五入
超出范圍,因為 f4(5,2),可以得知 整數位:3,小數位:2,1234.456 超出整數位的訪問了
INSERT INTO test_double1(f3,f4) VALUES(123.45,1234.456);
小數位超過就四舍五入,整數位超了就報錯了
3. 精度誤差說明
浮點數類型有個缺陷,就是不精準。下面我來重點解釋一下為什么 MySQL 的浮點數不夠精準。比如,我們設計一個表,有f1這個字段,插入值分別為0.47,0.44,0.19,我們期待的運行結果是:0.47 + 0.44 + 0.19 = 1.1。而使用sum之后查詢:
CREATE TABLE test_double2( f1 DOUBLE ); INSERT INTO test_double2 VALUES(0.47),(0.44),(0.19);
mysql> SELECT SUM(f1) -> FROM test_double2; +--------------------+ | SUM(f1) | +--------------------+ | 1.0999999999999999 | +--------------------+ 1 row in set (0.00 sec)
mysql> SELECT SUM(f1) = 1.1,1.1 = 1.1 -> FROM test_double2; +---------------+-----------+ | SUM(f1) = 1.1 | 1.1 = 1.1 | +---------------+-----------+ | 0 | 1 | +---------------+-----------+ 1 row in set (0.00 sec)
查詢結果是 1.0999999999999999。看到了嗎?雖然誤差很小,但確實有誤差。 你也可以嘗試把數據類型改成 FLOAT,然后運行求和查詢,得到的是, 1.0999999940395355。顯然,誤差更大了。
那么,為什么會存在這樣的誤差呢?問題還是出在 MySQL 對浮點類型數據的存儲方式上。
MySQL 用 4 個字節存儲 FLOAT 類型數據,用 8 個字節來存儲 DOUBLE 類型數據。無論哪個,都是采用二進制的方式來進行存儲的。比如 9.625,用二進制來表達,就是 1001.101,或者表達成 1.001101×2^3。如果尾數不是 0 或 5(比如 9.624),你就無法用一個二進制數來精確表達。進而,就只好在取值允許的范圍內進行四舍五入。
在編程中,如果用到浮點數,要特別注意誤差問題,因為浮點數是不準確的,所以我們要避免使用“=”來判斷兩個數是否相等。 同時,在一些對精確度要求較高的項目中,千萬不要使用浮點數,不然會導致結果錯誤,甚至是造成不可挽回的損失。那么,MySQL 有沒有精準的數據類型呢?當然有,這就是定點數類型:DECIMAL。
MySQL
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。