支付价格计算中精度问题之double,float
生活随笔
收集整理的這篇文章主要介紹了
支付价格计算中精度问题之double,float
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言
- 前段時間開發新的微信小程序,借此機會將老掉牙的支付模塊重構,并且支持現金支付(之前都是虛擬幣支付),在重構期間遇到計算上的一些精度問題,雖然數額影響非常小但是影響比較大,我覺得有必要總結以下遇到的一些問題,并且解決弄清楚他的原來,因此有下文。
先看幾個現象
- 當我們程序中涉及到一些double或者float類型的數據,并且精度要求比較高,小數點位數比較多的時候,可能會出現一些非常奇葩的問題,我下面針對遇到的一些問題給出幾個說明案例:
條件判斷異常
//比較特殊的案例 System.out.println(1f == 0.9999999f); //false System.out.println(1f == 0.99999999f); //true數據轉換異常
float f = 0.6f; double d1 = 0.6d; double d2 = f; System.out.println((d1 == d2) + " " + f + " " + d2);//false 0.6基本運算異常
System.out.println( 0.2 + 0.7 ); //0.8999999999999999數據自增異常
float f1 = 8455263f;for (int i = 0; i < 10; i++) {System.out.println(f1);f1++;} //8455263.0 //8455264.0 //8455265.0 //8455266.0 //8455267.0 //8455268.0 //8455269.0 //8455270.0 //8455271.0 //8455272.0float f2 = 84552631f;for (int i = 0; i < 10; i++) {System.out.println(f2);f2++;} //8.4552632E7 //8.4552632E7 //8.4552632E7 //8.4552632E7 //8.4552632E7 //8.4552632E7 //8.4552632E7 //8.4552632E7 //8.4552632E7 //8.4552632E7Java中浮點類型精度問題
- 要搞清除還是得從java的double和float類型來入手,我們都知道,計算機只認識二進制的0,1,那么在double和float中有整數部分,小數部分,如此說來那么應該會有一定的方式將小數轉為計算機認識的0,1 組合,這中方法是定義的一個轉換標準,其實而Java中浮點數采用的是IEEE 754標準
IEEE745 標準
- IEEE 745 是IEEE二進制浮點數算數標準(Standard for Floating-Point Arthmetic)的標準編號,等同一個國際標準ISO之類,是美國哪家公司訂的,這個標準定義來表示浮點數的格式(包括負零-0)與反常值(denormal number),一些特殊數值(例如無窮inf,非數值NaN)以及一些數值的“浮點數運算子”它知名來四種數值修約規則和五種例外狀況。還有要了解的可以去JDK官網
浮點數的組成結構
- 我們學習計算機組成原理的時候,應該學過的,Java中表示小數的時候有三個組成部分:
- 符號位 S
- 階碼部分 E
- 尾數部分 M
- 這三個緯度的信息,一個浮點數表示就可以完全確認下來,如下圖所示的存儲結構
- 符號位部分 S: 0 表示正數, 1 表示負數
- 階碼部分E (只整數部分):
- 對于float型浮點數,指數部分8 位,考慮可正負,因此可以表示的指數范圍是-127~128
- 對于double類型浮點數,指數部分11 為,可正負,因此可以表示的指數范圍是-1203~1024
- 尾數部分 M:
- 對于float類型來說,尾數23 為,計算成十進制就是2^23 = 8388608,所以十進制精度只有6~7位
- 對于double類型來說,尾數部分52位,計算成十進制就是2^52=4503599627370496,所以十進制的精度是15~16位
- 以上幾個都是官方的數據
總結
- 浮點數float和double在內存中是按照科學計數法來存儲的,取值范圍是由指數的位數來決定的,精度是由尾數的位數來決定的。
| float | 32bit 單精度 | 1bit(0正1負) | 8bit | -27 ~ 27-1(-128~127) | 2127(1038級別的數) | 23bit | 8388608,7位,精度為6~7位 |
| double | 64bit雙精度 | 1bit(0正1負) | 11bits | -210 ~ 210-1(-1024~1023) | 21023(10308級別的數) | 52bit | 45035_99627_37049_6,16位,精度為15~16位 |
浮點數和二進制數互相轉化
十進制浮點數如何用二進制表示
- 計算過程,小數部分,將小數部分乘以2,取出結果中整數部分作為二進制表示的第一位(大于等于1為1,小于1 為0),然后在將小數部分乘以2,得到整數部分作為二進制表示第二位…依次類推知道小數部分位0.
- 特殊情況永遠都不會位0:小數部分循環出現,無法停止,則用優先的二進制位無法表示這個小數,這也是在編程語言中小數位出現誤差的原因。
- 我們用如下的案例來說明這個過程10.6:
- 以上我們可以發現0.6 是一個無法精確表示的一個數值,用二進制表示1001 1001 1001 1001 …
- 那10.6 的二進制我們可以表示如下:1010.1001 1001 1001 …
二進制浮點數如何轉為十進制
- 計算過程:從左到右,v[i]*2(-i), i為從左到右的index,v[i]為該位的值,直接看例子如下:
- 10.6 的二進制1010.1001 1001 1001,從小數點位為基準如下:
- 0 * 20 + 1 * 21 + 0 * 22 + 1 * 23 = 0+2+0+8//(整數部分)
- 1 * 2-1 + 0 * 2-2 + 0 * 2-3 + 1 * 2-4 + 1 * 2-5 + 0 * 2-6 + 0 * 2-7 + 1 * 2-8 … = 0.5+0.0625 + 0.03125 ≈ 0.6 // 小數部分
問題解答
- 我們通過開始的案例,還有之前關于double,float這部分的分析,我們來解答一下最開始的哪些問題是怎么出現的:
float類型賦值給double類型變量出現精度問題
- 因為float的尾數23為,double尾數52位,所以float類型中保存的0.6 的二進制轉成double二進制的時候低位的二進制自動變成0 ,與用double類型保存的0.6的二進制是不一樣的,所以出問題來,如下圖來解釋:
- 如上如果用d2 和d1 比較他們肯定是不相等的
第一個案例分析
System.out.println( 1f == 0.99999999f );- 這個結果是true因為計算機無法區分這個兩個的二進制數,我們也來推到一些他們的二進制表示
- 如上,這第一個和第二個二進制數是一樣的,第三個是不一樣的,只是因為上面的0.99999999f 九個9 明顯超過來float類型的精度范圍湊巧和1 是一樣的就出現這種問題。
浮點計算
- Java當中默認聲明的小數是double類型的,其默認后綴"d"或"D"可以省略,如果要聲明為float類型,需顯示添加后綴"f"或"F"
- 我們盡量用BigDecial來計算
總結
以上是生活随笔為你收集整理的支付价格计算中精度问题之double,float的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 放血拔罐的好处和坏处
- 下一篇: Zookeeper--Watcher机制