一、
三者的區別介紹
float:浮點型,含字節數為4,32bit,數值范圍為-3.4E38~3.4E38(7個有效位)
double:雙精度實型,含字節數為8,64bit數值范圍-1.7E308~1.7E308(15個有效位)
decimal:數字型,128bit,不存在精度損失,常用於銀行帳目計算。(28個有效位)
單精度浮點數在機內占4個字節,用32位二進制描述。
雙精度浮點數在機內占8個字節,用64位二進制描述。
浮點數在機內用指數型式表示,分解為:數符,尾數,指數符,指數四部分。
數符占1位二進制,表示數的正負。
指數符占1位二進制,表示指數的正負。
尾數表示浮點數有效數字,0.xxxxxxx,但不存開頭的0和點
指數存指數的有效數字。
指數占多少位,尾數占多少位,由計算機系統決定。
可能是數符加尾數占24位,指數符加指數占8位 -- float.
數符加尾數占48位,指數符加指數占16位 -- double.
知道了這四部分的占位,按二進制估計大小范圍,再換算為十進制,就是你想知道的數值范圍。
對編程人員來說,double 和 float 的區別是double精度高,有效數字16位,float精度7位。但double消耗內存是float 的兩倍,double的運算速度比float慢得多,C語言中數學函數名稱double 和 float不同,不要寫錯,能用單精度時不要用雙精度(以省 內存,加快運算速度)。
總結:對於單精度浮點數Float: 當數據范圍在+-131072(65536×2)以內的時候,float數據精度是正確的,但是超出這個范圍的數據就不穩定,沒有發現有相關的參數設置建議:將float改成double或者decimal,兩者的差別是double是浮點計算,decimal是定點計算,會得到更精確的數據。
分析如下:
一、浮點數的概念及誤差問題:
浮點數是用來表示實數的一種方法,它用 M(尾數) * B( 基數)的E(指數)次方來表示實數,相對於定點數來說,在長度一定的情況下,具有表示數據范圍大的特點。但同時也存在誤差問題,這就是著名的浮點數精度問題!
浮點數有多種實現方法,計算機中浮點數的實現大都遵從 IEEE754 標准,IEEE754 規定了單精度浮點數和雙精度浮點數兩種規格,單精度浮點數用4字節(32bit)表示浮點數,格式是:1位符號位 8位表示指數 23位表示尾數;雙精度浮點數8字節(64bit)表示實數,格式是:1位符號位 11位表示指數 52位表示尾數。同時,IEEE754標准還對尾數的格式做了規范:d.dddddd...,小數點左面只有1位且不能為零,計算機內部是二進制,因此,尾數小數點左面部分總是1。顯然,這個1可以省去,以提高尾數的精度。由上可知,單精度浮點數的尾數是用24bit表示的,雙精度浮點數的尾數是用53bit表示的,轉換成十進制:
2^24 - 1 = 16777215
2^53 - 1 = 9007199254740991
由上可見,IEEE754單精度浮點數的有效數字二進制是24位,按十進制來說,是8位;雙精度浮點數的有效數字二進制是53位,按十進制來說,是16 位。
顯然,如果一個實數的有效數字超過8位,用單精度浮點數來表示的話,就會產生誤差!同樣,如果一個實數的有效數字超過16位,用雙精度浮點數來表示,也會產生誤差!
例如:對於 1310720000000000000000.66 這個數,有效數字是24位,用單精度或雙精度浮點數表示都會產生誤差,只是程度不同:
單精度浮點數: 1310720040000000000000.00
雙精度浮點數: 1310720000000000000000.00
雙精度差了 0.66 ,單精度差了近4萬億!以上說明了因長度限制而造成的誤差,但這還不是全部!采用IEEE754標准的計算機浮點數,在內部是用二進制表示的,但在將一個十進制數轉換為二進制浮點數時,也會造成誤差,原因是不是所有的數都能轉換成有限長度的二進制數。對於131072.32 這個數,其有效數字是8位,按理應該能用單精度浮點數准確表示,為什么會出現偏差呢?看一下這個數據二進制尾數就明白了 10000000000000000001010001......顯然,其尾數超過了24bit,根據舍入規則,尾數只取 100000000000000000010100,結果就造成測試中遇到的“奇怪”現象!131072.68 用單精度浮點數表示變成 131072.69 ,原因與此類似。實際上有效數字小於8位的數,浮點數也不一定能精確表示,7.22這個數的尾數就無法用24bit二進制表示,當然在數據庫中測試不會有問題(舍入以后還是7.22),但如果參與一些計算,誤差積累后,就可能產生較大的偏差。
二、mysql 和 oracle中的數值類型:
問題是不是只有 mysql 存在呢?
顯然不是,只要是符合IEEE754標准的浮點數實現,都存在相同的問題。
mysql中的數值類型(不包括整型):
IEEE754 浮點數: float(單精度),double或 real(雙精度)
定點數: decimal 或 numeric
oracle中的數值類型:oracle 浮點數 :number (注意不指定精度)
IEEE754浮點數: BINARY_FLOAT(單精度) ,BINARY_DOUBLE (雙精度)
FLOAT,FLOAT(n) (ansi要求的數據類型) 定點數: number(p,s)
如果在oracle中,用BINARY_FLOAT等來做測試,結果是一樣的。
因此,在數據庫中,對於涉及貨幣或其他精度敏感的數據,應使用定點數來存儲,對mysql來說是 decimal,對oracle來說就是number(p,s)。雙精度浮點數,對於比較大的數據同樣存在問題!
三、編程中也存在浮點數問題:
不光數據庫中存在浮點數問題,編程中也同樣存在,甚至可以說更值得引起注意!
通過上面的介紹,浮點數的誤差問題應該比較清楚了。如果在程序中做復雜的浮點數運算,誤差還會進一步放大。因此,在程序設計中,如果用到浮點數,一定要意識到可能產生的誤差問題。不僅如此,浮點數如果處理不好,還會導致程序BUG!
看下面的語句:
if (x != y)
{
z = 1 / (x -y);
}
這個語句看起來沒有問題,但如果是浮點數,就可能存在問題!
再看下面的語句會輸出什么結果:
public class Test
{
public static void main(String[]args)
throws Exception
{
System.out.print("7.22-7.0=" + (7.22f-7.0f));
}
}
我們可能會想當然地認為輸出結果應該是 0.22 ,實際結果卻是 0.21999979 !
因此,在編程中應盡量避免做浮點數的比較,否則可能會導致一些潛在的問題!
除了這些,還應注意浮點數中的一些特殊值,如 NaN、+0、-0、+無窮、-無窮等,IEEE754雖然對此做了一些約定,但各具體實現、不同的硬件結構,也會有一些差異,如果不注意也會造成錯誤!
四、總結:
從上面的分析,我們可以得出以下結論:
1、浮點數存在誤差問題;
2、對貨幣等對精度敏感的數據,應該用定點數表示或存儲;
3、編程中,如果用到浮點數,要特別注意誤差問題,並盡量避免做浮點數比較;
4、要注意浮點數中一些特殊值的處理。