經典的精度丟失問題

Java中的類型float、double用來作計算會有精度丟失問題,下面來看下面的示例。java

  1. public static void main(String[] args) {數據庫

  2.    test1();spa

  3.    test2();code

  4. }ci

  5.  

  6. private static void test1() {class

  7.    double totalAmount = 0.09;test

  8.    double feeAmount = 0.02;二進制

  9.    double tradeAmount = totalAmount - feeAmount;float

  10.    System.out.println(tradeAmount);程序

  11. }

上面的程序輸出結果是多少?

0.07?非也!

正確的結果是:

  1. 0.06999999999999999

爲何是這樣?

浮點數可能丟失精度,浮點十進制數一般沒有徹底相同的二進制的表示形式,這是CPU所採用的浮點數據表示形式的反作用。爲此,可能會有一些精度丟失,而且一些浮點運算可能會產生未知的結果。

浮點運算不多是精確的,只要是超過精度能表示的範圍就會產生偏差。因此,在使用float、double做精確運算的時候必定要特別當心,除非能容忍精度丟失,否則產生的偏差也是會形成雙方對帳不一致的結果。

怎麼解決

在《Effective Java》這本書中也提到這個原則,float和double只能用來作科學計算或者是工程計算,在商業計算中咱們要用 java.math.BigDecimal。

BigDecimal適合更精度的運算,也提供了豐富的操做符類型,小數位控制,四捨五入規則等。

不過,使用BigDecimal不當也有精度丟失的狀況,如double的構造方法:

  1. BigDecimal(double val)

再來看這個示例:

  1. private static void test2() {

  2.    double totalAmount = 0.09;

  3.    double feeAmount = 0.02;

  4.    BigDecimal tradeAmount = new BigDecimal(totalAmount).subtract(new BigDecimal(feeAmount));

  5.    System.out.println(tradeAmount);

  6. }

輸出:

  1. 0.0699999999999999962529972918900966760702431201934814453125

這個精度就更恐怖了。。

因此,必定要使用String的構造方法:

  1. BigDecimal(String val)

  1. private static void test3() {

  2.    double totalAmount = 0.09;

  3.    double feeAmount = 0.02;

  4.    BigDecimal tradeAmount = new BigDecimal(String.valueOf(totalAmount))

  5.            .subtract(new BigDecimal(String.valueOf(feeAmount)));

  6.    System.out.println(tradeAmount);

  7. }

總結

  • 金額運算儘可能使用BigDecimal(String val)進行運算。

  • 數據庫存儲金額,通常有整型和浮點型兩種存儲方式。若是是有匯率轉換的,建議使用浮點數decimal進行存儲,能夠靈活的控制精度,decimal直接對應java類型BigDecimal。固然,用整數存儲分這種形式也能夠,轉帳的時候單位爲元而若是忘了轉換分爲元,那就悲劇了。

相關文章
相關標籤/搜索