天天看點

BigDecimal類中的double類型值的加減問題-java

了解到BigInteger類可以存入無限大小的值,進而可以用它進行大數值計算.

BigInteger中的構造方法中的一個參數是沒有可以直接接受int,double,等基本資料類型,一個參數是接受String類型.

但是,BigDecimal類是可以接受double,int,long,String類型的值.問題就由此出現了.

問題:

import java.math.BigDecimal;

public class BigDecimalDemo {
  public static void main(String[] args) {
    System.out.println(1.1+1.2101);
    BigDecimal bd1 = new BigDecimal(1.1);
    BigDecimal bd2 = new BigDecimal(1.2101);
    System.out.println(bd1.add(bd2));
  }
}
/*輸出:
2.3101000000000003
2.3101000000000000422772927777259610593318939208984375
*/      

BigDecimal(摘自Java SE8 API文檔)

public BigDecimal(double val)Translates a double into a BigDecimal which is the exact decimal representation of the double’s binary floating-point value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer.

Notes:

  1.The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

  2.The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal(“0.1”) creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

  3.When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

Parameters:

val - double value to be converted to BigDecimal.

Throws:

NumberFormatException - if val is infinite or NaN.

 文檔中說的很明白,對于Decimal中double構造器,該構造器的結果可能有些不可預料.這是因為0.1不能精确地表示為雙精度(或者,就此而言,不能表示為任何有限長度的二進制分式)。是以,傳遞給構造函數的值并不完全等于0.1,盡管看起來是這樣。

 推薦使用String構造器,它與所存的double類型的字元串所預料的是一樣的.

 可以看看這篇文章:​​​浮點數存儲​​.

解決辦法:

import java.math.BigDecimal;

public class BigDecimalDemo {
  public static void main(String[] args) {
    bd1 = new BigDecimal("1.1");
    bd2 = new BigDecimal("1.2101");
    System.out.println(bd1.add(bd2));
    System.out.println(BigDecimal.valueOf(1.1).add(BigDecimal.valueOf(1.2101)));
  }
}
/*輸出:
2.3101
2.3101
*/      
  1. 使用​

    ​BigDecimal(String val)​

    ​​構造器
  2. 先将double類型的值使用​

    ​public static BigDecimal valueOf(double val)​

    ​​方法.

    原理:将double類型的值先轉換為String類型的值,在存儲到BigDecimal對象中.

BigDecimal.valueOf(double val)源碼:

public static BigDecimal valueOf(double val) {
       // Reminder: a zero double returns '0.0', so we cannot fastpath
       // to use the constant ZERO.  This might be important enough to
       // justify a factory approach, a cache, or a few private
       // constants, later.
       return new BigDecimal(Double.toString(val));
   }      

API中0.1 (an unscaled value of 1, with a scale of 1)的了解

API文檔中有一句(an unscaled value of 1, with a scale of 1)該如何了解呢?

BigDecimal類中有​

​unscaledValue()​

​​和​

​scale()​

​方法.通過這兩個方法來了解這句話吧.

unscaledValue

public BigInteger unscaledValue()

Returns a BigInteger whose value is the unscaled value of this BigDecimal. (Computes (this * 10this.scale()).)

Returns:

the unscaled value of this BigDecimal.

Since:

1.2

scale

public int scale()

Returns the scale of this BigDecimal. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. For example, a scale of -3 means the unscaled value is multiplied by 1000.

Returns:

the scale of this BigDecimal.

方法應用

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;

public class FoctorialDemo {
  public static void main(String[] args) {
    BigDecimal bd = new BigDecimal("-1.2101");  
    System.out.println(bd.scale());
    System.out.println(bd.unscaledValue());
  }
}
/*輸出:
4
-12101
*/