web-dev-qa-db-ja.com

JavaのBigdecimal.divideと丸め

職場で、大きな数値を1000で除算しようとしたときに問題が見つかりました。この数値はデータベースに由来します。

この方法があるとしましょう:

private static BigDecimal divideBy1000(BigDecimal dividendo) {
    if (dividendo == null) return null;

    return dividendo.divide(BigDecimal.valueOf(1000), RoundingMode.HALF_UP);
}

次の電話をかけるとき

divideBy1000(new BigDecimal("176100000"))

176100の期待値を受け取ります。しかし、以下の行を試してみると

divideBy1000(new BigDecimal("1761e+5"))

値200000を受け取ります。これはなぜ発生しますか?両方の数字は異なる表現で同じであり、最新のものはデータベースから受け取ったものです。どういうわけか、JVMは数1761を1000で割って、端数を切り上げて0で埋めていることを理解しています。

この種の動作を回避する最良の方法は何ですか?元の番号は私によって管理されていないことに注意してください。

26
Raphael do Vale

Javadocで指定されているように、BigDecimalは整数値とscaleで定義されます。

したがって、BigDecimalで表される数値の値は(unscaledValue×10 ^(-scale))です。

したがって、BigDecimal("1761e+5")のスケールは-5で、BigDecimal(176100000)のスケールは0です。

2つのBigDecimalの除算は、除算時にスケールが指定されていないため、-5スケールと0スケールを使用して行われます。 divide documentation は、結果が異なる理由を説明しています。

divide

_public BigDecimal divide(BigDecimal divisor)
_

BigDecimal を返します。値は_(this / divisor)_で、優先スケールは_(this.scale() - divisor.scale())_;です。正確な商を表現できない場合(終了しない10進数の展開があるため)、ArithmeticExceptionがスローされます。

パラメータ:

divisor-このBigDecimalを分割する値。

戻り値:

_this / divisor
_

スロー:

ArithmeticException —正確な商に終端の10進数展開がない場合

以来:

1.5

分割時にスケールを指定した場合、例えばdividendo.divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP)同じ結果が得られます。

21
dcernahoschi

new BigDecimal("176100000")およびnew BigDecimal("1761e+5")は、等しくないです。 BigDecimalは、値と精度の両方を追跡します。

BigDecimal("176100000")の精度は9桁で、内部的にはBigInteger("176100000")として表され、1倍されます。BigDecimal("1761e+5")は4桁の精度であり、内部的にはBigInteger("1761")、100000倍。

BigDecimalを値で除算すると、結果は精度の桁を尊重し、一見等しい値に対して異なる出力をもたらします。

6
Joshua

bigDecimalであなたの部門に。

dividendo.divide(divisor,2,RoundingMode.CEILING)//00.00 nothing for up and nothing for down

この操作では、小数点以下2桁の精度があります。

2
leandro lion

ええ、それはあなたが実験している問題の一種です。私がそうするかもしれないならば、あなたが指数数だけを持っている状況で、あなたはそれらを投げて、そしてあなたの方法を使うべきです。私が提案するのは、このコードのほんの一部です。

long  longValue = Double.valueOf("1761e+5").longValue();
BigDecimal value= new BigDecimal(longValue);

これらの文字列を新しいBigDecimalに変換し、このBigDecimal値を返すメソッドで使用します。その後、divideBy1000でこれらの返された値を使用できます。これにより、発生している問題がすべて解決されます。

それらがたくさんある場合は、リストのようなデータ構造にそれらのBigDecimalを保存することでもできます。次に、foreachループを使用します。このループでは、divideBy1000を適用すると、新しい値がそれぞれ別のリストに保存されます。次に、このリストにアクセスして、新しい値のセットを取得する必要があります。

それが役に立てば幸い :)

0
Kevin Avignon

BigDecimalに10の累乗を掛けるときはいつでも、この場合は10を掛けます-3dividendo.scaleByPowerOfTen(power)を使用して、BigDecimalオブジェクトのスケールのみを変更し、丸めの問題をサイドステップするか、少なくとも後の計算に移動することができます。

ここでの他の回答は、任意の数で割るより一般的なケースをカバーしています。

0
meticoeus

round()を使用してみてください。

_private static BigDecimal divideBy1000(BigDecimal dividendo) {
    if (dividendo == null) return null;

    return dividendo.divide(BigDecimal.valueOf(1000)).round(new MathContext(4, RoundingMode.HALF_UP));
}

 public static void main(String []args){
    BigDecimal bigD = new BigDecimal("1761e5");
    BigDecimal bigDr = divideBy1000(bigD);
    System.out.println(bigDr);
 }
_

new MathContext(4, RoundingMode.HALF_UP))行は、除算を4桁に戻します。

これにより、以下が生成されます。

_1.761E+5
_

これはあなたが望むものです。 (:

0