日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

支付价格计算中精度问题之double,float

發布時間:2023/12/4 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 支付价格计算中精度问题之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.4552632E7

Java中浮點類型精度問題

  • 要搞清除還是得從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在內存中是按照科學計數法來存儲的,取值范圍是由指數的位數來決定的,精度是由尾數的位數來決定的。
浮點數精度/位數符號S指數E擴展范圍 (指數的取值范圍)最大/小值(取值范圍)尾數位M尾數取值范圍(精度)
float32bit 單精度1bit(0正1負)8bit-27 ~ 27-1(-128~127)2127(1038級別的數)23bit8388608,7位,精度為6~7位
double64bit雙精度1bit(0正1負)11bits-210 ~ 210-1(-1024~1023)21023(10308級別的數)52bit45035_99627_37049_6,16位,精度為15~16位

浮點數和二進制數互相轉化

十進制浮點數如何用二進制表示
  • 計算過程,小數部分,將小數部分乘以2,取出結果中整數部分作為二進制表示的第一位(大于等于1為1,小于1 為0),然后在將小數部分乘以2,得到整數部分作為二進制表示第二位…依次類推知道小數部分位0.
  • 特殊情況永遠都不會位0:小數部分循環出現,無法停止,則用優先的二進制位無法表示這個小數,這也是在編程語言中小數位出現誤差的原因。
  • 我們用如下的案例來說明這個過程10.6:
0.6*2=1.2 ---- 1 0.2*2=0.4 ---- 0 0.4*2=0.8 ---- 0 0.8*2=1.6 ---- 1 0.6*2=1.2 ---- 1 0.2*2=0.4 ---- 0 . . . 0.6*2=1.2 ---- 1 0.2*2=0.4 ---- 0
  • 以上我們可以發現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的二進制是不一樣的,所以出問題來,如下圖來解釋:
float 類型的0.6f: 1001 1001 1001 1001 1001 100 double類型的d1 = 0.6d: 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 將float類型f 賦值給double類型的d2后,d2 的實際數據位: 1001 1001 1001 1001 1001 1000 0000 0000 0000 0000 0000 0000 0000
  • 如上如果用d2 和d1 比較他們肯定是不相等的
第一個案例分析
System.out.println( 1f == 0.99999999f );
  • 這個結果是true因為計算機無法區分這個兩個的二進制數,我們也來推到一些他們的二進制表示
1.0(十進制)↓ 00111111 10000000 00000000 00000000(二進制)↓ 0x3F800000(十六進制)0.99999999(十進制)↓ 00111111 10000000 00000000 00000000(二進制)↓ 0x3F800000(十六進制)0.9999999(十進制)↓ 00111111 01111111 11111111 11111110(二進制)↓ 0x3F7FFFFE(十六進制)
  • 如上,這第一個和第二個二進制數是一樣的,第三個是不一樣的,只是因為上面的0.99999999f 九個9 明顯超過來float類型的精度范圍湊巧和1 是一樣的就出現這種問題。
浮點計算
  • Java當中默認聲明的小數是double類型的,其默認后綴"d"或"D"可以省略,如果要聲明為float類型,需顯示添加后綴"f"或"F"
  • 我們盡量用BigDecial來計算

總結

以上是生活随笔為你收集整理的支付价格计算中精度问题之double,float的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。