インタビュー:BigDecimalは間違いなく精度を失わないのですか?

序文

浮動小数点変数は計算時に精度が失われることは誰もが知っています。次のコード:

System.out.println(0.05 + 0.01); 
System.out.println(1.0-0.42); 
System.out.println(4.015 * 100); 
System.out.println(

123.3 / 100);システム出:
0.060000000000000005 
0.5800000000000001 
401.49999999999994 
1.2329999999999999

Javaで浮動小数点演算を実行すると、精度が低下するという問題が発生することがわかります。次に、商品価格を計算すると問題が発生します。手元に0.06元ある可能性が高いですが、0.05元と0.01元の商品は購入できません。上に示したように、2つの合計は0.060000000000000005であるためです。これは間違いなく非常に深刻な問題であり、特にeコマースWebサイトの同時実行性が高まると、問題は非常に大きくなります。注文の失敗や和解の問題が発生する可能性があります。したがって、次に、JavaのBigDecimalクラスを使用して、この種の問題を解決できます。

普及させる:

Javaのfloatの精度は、6〜7桁です。doubleの精度は15〜16桁です。

API

コンストラクタ:

コンストラクター説明                      
  BigDecimal(int)パラメーターで指定された整数値でオブジェクトを作成します。      
  BigDecimal(double)は、パラメーターで指定された倍精度値を使用してオブジェクトを作成します。     
  BigDecimal(long)は、パラメーターで指定された長整数値でオブジェクトを作成します。     
  BigDecimal(String)は、パラメーターで指定された文字列で表される値でオブジェクトを作成します。


関数:

 メソッド説明                         
  add(BigDecimal)BigDecimalオブジェクトに値を追加してから、このオブジェクトを返します。
  減算(BigDecimal)BigDecimalオブジェクトの値を減算して、このオブジェクトを返します。
  multiply(BigDecimal)BigDecimalオブジェクトの値を乗算してから、このオブジェクトを返します。
  split(BigDecimal)BigDecimalオブジェクトの値を除算してから、このオブジェクトを返します。
  toString()BigDecimalオブジェクトの数値を文字列に変換します。    
  doubleValue()は、BigDecimalオブジェクトの値を倍精度の数値として返します。   
  floatValue()は、BigDecimalオブジェクトの値を単一精度の数値として返します。   
  longValue()は、BigDecimalオブジェクトの値を長整数として返します。    
  intValue()は、BigDecimalオブジェクトの値を整数として返します。


たとえば、一般的な数値タイプのため、doubleは16桁を超える数値を正確に表すことはできません。

BigDecimalの精度も失われます

BigDecimalを使用する場合、BigDecimal(String)コンストラクターを使用してオブジェクトを作成することは理にかなっています。BigDecimal b = new BigDecimal(1)などの他の方法でも、精度が低下するという問題があります。次のコード:

BigDecimal a = new BigDecimal(1.01); 
BigDecimal b = new BigDecimal(1.02); 
BigDecimal c = new BigDecimal( "1.01"); 
BigDecimal d = new BigDecimal( "1.02"); 
System.out.println(a.add(b)); 
System.out.println(c.add(d));

出力:
2.0300000000000000266453525910037569701671600341796875 
2.03


BigDecimalの精度の低下はさらに深刻であることがわかります。ただし、BigDecimalのBigDecimal(String)コンストラクター変数を使用する場合、そのような問題はありません。その理由はコンピューター構成の原則に含まれており、それらのコーディングがこの結果を決定します。longは19桁を正確に格納できますが、doubleは16桁しか格納できません。doubleにはexpビットがあるため、16桁以上を格納できますが、下位の不正確さを犠牲にする必要があります。19桁を超える正確なストレージが必要な場合は、BigIntegerを使用して保存する必要があります。もちろん、パフォーマンスがいくらか犠牲になります。したがって、一般的にBigDecimalを使用して商用操作での精度の低下の問題を解決する場合、BigDecimalオブジェクトを宣言するときに、パラメーターがStringであるコンストラクターを構築するために使用する必要があります。

同時に、この原則は、EffectiveJavaおよびMySQLMust Know MustKnowにも記載されています。フロートとダブルは、科学的計算と工学的計算にのみ使用できます。ビジネスコンピューティングではBigDecimalを使用する必要があります。

そして、ソースコードのコメントから正式に指示を出しました。以下は、BigDecimalクラスのdouble型パラメーターのコンストラクターの注釈の一部です。

* The results of this constructor can be somewhat unpredictable.
     * One might assume that writing {@code new BigDecimal(0.1)} in
     * Java creates a {@code 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
     * {@code double} (or, for that matter, as a binary fraction of
     * any finite length).  Thus, the value that is being passed
     * <i>in</i> to the constructor is not exactly equal to 0.1,
     * appearances notwithstanding.
       ……
        * When a {@code double} must be used as a source for a
     * {@code BigDecimal}, note that this constructor provides an
     * exact conversion; it does not give the same result as
     * converting the {@code double} to a {@code String} using the
     * {@link Double#toString(double)} method and then using the
     * {@link #BigDecimal(String)} constructor.  To get that result,
     * use the {@code static} {@link #valueOf(double)} method.
     * </ol>
public BigDecimal(double val) {
    this(val,MathContext.UNLIMITED);
}


第一段也说的很清楚它只能计算的无限接近这个数,但是无法精确到这个数。第二段则说,如果要想准确计算这个值,那么需要把double类型的参数转化为String类型的。并且使用BigDecimal(String)这个构造方法进行构造。 去获取结果。

正确运用BigDecimal

另外,BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象,由刚才我们所罗列的API也可看出。

在一般开发过程中,我们数据库中存储的数据都是float和double类型的。在进行拿来拿去运算的时候还需要不断的转化,这样十分的不方便。这里我写了一个工具类:

/**
 * @author: Ji YongGuang.
 * @date: 19:50 2017/12/14.
 */
public class BigDecimalUtil {

    private BigDecimalUtil() {

    }

    public static BigDecimal add(double v1, double v2) {// v1 + v2
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2);
    }

    public static BigDecimal sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2);
    }

    public static BigDecimal mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2);
    }

    public static BigDecimal div(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        // 2 = 保留小数点后两位   ROUND_HALF_UP = 四舍五入
        return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);// 应对除不尽的情况
    }
}


该工具类提供了double类型的基本的加减乘除运算。直接调用即可。

最后

感谢大家看到这里,文章有不足,欢迎大家指出;如果你觉得写得不错,那就给我一个赞吧。


おすすめ

転載: blog.51cto.com/14849432/2540536
おすすめ