BigDecimalがピットサマリーを踏む

文字列型でBigDecimalを初期化します

java.mathパッケージでJavaによって提供されるAPIクラスBigDecimalは、有効数字が16桁を超える数値に対して正確な演算を実行するために使用されます。bigDecimalを初期化するときは、double型またはfloat型の値を使用してコンストラクターに渡さないでください。比較では、test2には依然として精度の問題があることが示されていますが、BigDecimalオブジェクトを作成するときに、パラメーターが文字列の場合、精度はありません。問題があるので、要約は次のとおりです

//使用String构造
BigDecimal b1 = new BigDecimal("0.1");
//或者是:
BigDecimal b1 = BigDecimal.valueOf(0.1);
//点开valueOf的源码,可以看到在源码中也是用new BigDecimal(String);返回一个BigDecimal对象的
//源码如下:
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));
}
复制代码

BigDecimal数値比較を行うときは、比較にequalsを使用しないでください

equalsを使用して比較すると、値のサイズと精度のサイズが比較されます。つまり、0.00と0.000が等しくない場合は、compareTo()を使用して比較します。

計算を実行するときは、計算に関与する値がnullにならないようにする必要があります

計算にBigDecimalタイプを使用する場合、サイズの加算、減算、乗算、除算、および比較を行う場合は、計算に含まれる2つの値が空にならないようにしてください。空にしないと、例外がスローされjava.lang.NullPointerExceptionます。

たとえば、次の2つのコードは例外をスローします。

BigDecimal number1 = null;
BigDecimal number2 = new BigDecimal("11.12");

BigDecimal number3 = number1.add(number2);
System.out.println("number1 add number2 = " + number3);

BigDecimal number1 = new BigDecimal("88.88");
BigDecimal number2 = null;

BigDecimal number3 = number1.add(number2);
System.out.println("number1 add number2 = " + number3);
复制代码

スローされる例外は次のとおりです。

divideメソッドを使用するときは、無限ループの小数部と除数部を0にすることはできないことに注意してください

このメソッドを1回使用BigDecimalするdivideと、例外がスローされjava.lang.ArithmeticException、エラーコードは次のようになります。

// 含税金额
BigDecimal inclusiveTaxAmount = new BigDecimal("1000");
// 税率
BigDecimal taxRate = new BigDecimal("0.13");
// 不含税金额 = 含税金额 / (1+税率)
BigDecimal exclusiveTaxAmount = inclusiveTaxAmount.divide(BigDecimal.ONE.add(taxRate));

System.out.println(exclusiveTaxAmount);
复制代码

ランタイムは次の例外をスローします。

エラーの理由は、除算できないため、小数の無限ループが発生するためです。

解決策は、最も一般的な丸めモードのように、切り捨てモードを指定することです。

// 不含税金额 = 含税金额 / (1+税率)
BigDecimal exclusiveTaxAmount = inclusiveTaxAmount.divide(BigDecimal.ONE.add(taxRate),RoundingMode.HALF_UP);
复制代码

この時点では、エラーは報告されておらず、出力は次のとおりです。885

ただし、ここでの私の要件は、小数点以下2桁を保持して切り上げることであるため、コードは次のようになります。

// 不含税金额 = 含税金额 / (1+税率)
BigDecimal exclusiveTaxAmount = inclusiveTaxAmount.divide(BigDecimal.ONE.add(taxRate), 2, RoundingMode.HALF_UP);
复制代码

この時点での出力は次のとおりです。884.96

BigDecimalからString、科学的記数法の問題

結論:BigDecimalをStringに変換するには、toString()の代わりにtoPlainString()を使用することをお勧めします。

多分あなたはそれをこのように書いた:

BigDecimal amount = new BigDecimal("3450.67");
System.out.println(amount.toString());
复制代码

出力結果:3450.67

ほとんどの場合、この使用法に問題はありませんが、次の3つの例のように、ピットを踏むのは簡単なシナリオもあります。

System.out.println( new BigDecimal("0.000000000000").toString());
//输出结果:0E-12

BigDecimal bigDecimal = new BigDecimal("1E+11");
System.out.println(bigDecimal.toString());
//输出结果:1E+11
复制代码

还有个更为常用的场景:抹零,也容易踩坑,比如下面所示的代码,预期的输出结果是3550,但实际上并不是:

BigDecimal bigDecimal = new BigDecimal("3550.00");
System.out.println(bigDecimal.stripTrailingZeros().toString());
//输出结果:3.55E+3

//使用toPlainString()方法可以避免这个问题,如下所示:
System.out.println( new BigDecimal("0.000000000000").toPlainString());
System.out.println( new BigDecimal("1E+11").toPlainString());
System.out.println(new BigDecimal("3550.00").stripTrailingZeros().toPlainString());

//输出结果:
//0.000000000000
//100000000000
//3550
复制代码

其实,BigDecimal提供了3个转换为String的方法,分别为:

  1. toString() 某些场景下使用科学计数法

  2. toPlainString() 不使用任何计数法

  3. toEngineeringString() 某些场景下使用工程计数法

这里简单提下科学计数法和工程计数法的区别:

科学记数法,是将数字表示成10的幂的倍数的形式。

工程记数法,是在科学记数法基础上,将10的幂限制为3的倍数。

举例:

原始值 科学技术法 工程计数法
2700 2.7 x 10³ 2.7 x 10³
27000 2.7 x 10⁴ 27 x 10³
270000 2.7 x 10⁵ 270 x 10³
2700000 2.7 x 10⁶ 2.7 x 10⁶

示例代码:

BigDecimal bigDecimal = new BigDecimal("270000.00").stripTrailingZeros();
System.out.println(bigDecimal.toString());
System.out.println(bigDecimal.toPlainString());
System.out.println(bigDecimal.toEngineeringString());

//输出结果:
//2.7E+5
//270000
//270E+3
复制代码

使用规范

尽量不要在项目中使用new BigDecimal("0"),而是使用BigDecimal提供的常量BigDecimal.ZERO

BigDecimal zero = BigDecimal.ZERO;
BigDecimal one = BigDecimal.ONE;
BigDecimal ten = BigDecimal.TEN;
复制代码

おすすめ

転載: juejin.im/post/7079982213856493604