85

減算が何らかの問題を引き起こし、結果の値が間違っているようです。

double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d;

78.75 = 787.5 * 10.0/100d

double netToCompany = targetPremium.doubleValue() - tempCommission;

708.75 = 787.5 - 78.75

double dCommission = request.getPremium().doubleValue() - netToCompany;

877.8499999999999 = 1586.6 - 708.75

結果の期待値は 877.85 になります。

正確な計算を確実にするために何をすべきですか?

4

13 に答える 13

105

浮動小数点演算の精度を制御するには、java.math.BigDecimalを使用する必要があります。詳細については、JohnZukowskiによるBigDecimalの必要性をお読みください。

あなたの例では、最後の行はBigDecimalを使用して次のようになります。

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

これにより、次の出力が得られます。

877.85 = 1586.6 - 708.75
于 2008-10-07T17:09:27.547 に答える
76

前の回答が述べたように、これは浮動小数点演算を行った結果です。

以前のポスターが示唆したように、数値計算を行うときは、 を使用しますjava.math.BigDecimal

ただし、使用には落とし穴がありBigDecimalます。double 値から に変換する場合、新しいコンストラクターまたは静的ファクトリ メソッドBigDecimalを使用するかを選択できます。静的ファクトリ メソッドを使用します。BigDecimal(double)BigDecimal.valueOf(double)

double コンストラクターは の精度全体doubleを aBigDecimalに変換しますが、静的ファクトリは効果的にそれを aStringに変換してから、それを a に変換しBigDecimalます。

これは、これらの微妙な丸め誤差に遭遇したときに関連します。数値は .585 と表示される場合がありますが、内部的にはその値は '0.58499999999999996447286321199499070644378662109375' です。コンストラクターを使用するBigDecimalと、0.585 に等しくない数値が得られますが、静的メソッドでは 0.585 に等しい値が得られます。

ダブル値 = 0.585;
System.out.println(新しい BigDecimal(値));
System.out.println(BigDecimal.valueOf(値));

私のシステムでは

0.58499999999999996447286321199499070644378662109375
0.585
于 2008-10-07T17:20:34.403 に答える
10

もう一つの例:

double d = 0;
for (int i = 1; i <= 10; i++) {
    d += 0.1;
}
System.out.println(d);    // prints 0.9999999999999999 not 1.0

代わりにBigDecimalを使用してください。

編集:

また、これは「Java」の丸めの問題ではないことを指摘するだけです。他の言語も同様の(必ずしも一貫しているとは限りませんが)動作を示します。Javaは、少なくともこの点で一貫した動作を保証します。

于 2008-10-07T17:14:13.120 に答える
8

上記の例を次のように変更します。

import java.math.BigDecimal;

BigDecimal premium = new BigDecimal("1586.6");
BigDecimal netToCompany = new BigDecimal("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

このようにして、最初から文字列を使用することの落とし穴を回避できます。別の方法:

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf(158660, 2);
BigDecimal netToCompany = BigDecimal.valueOf(70875, 2);
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

これらのオプションは、double を使用するよりも優れていると思います。とにかく、webapps の数字は文字列として始まります。

于 2009-04-30T20:42:03.287 に答える
7

double を使用して計算を行うときはいつでも、これが発生する可能性があります。このコードは 877.85 になります:

二重の答え = Math.round(dCommission * 100000) / 100000.0;

于 2008-10-07T17:04:26.933 に答える
4

ドルではなくセント数を節約し、出力時にドルにフォーマットするだけです。そうすれば、精度の問題に悩まされない整数を使用できます。

于 2008-10-07T17:09:40.307 に答える
3

これは楽しい問題です。

Timonsの応答の背後にある考え方は、リーガルダブルが可能な最小の精度を表すイプシロンを指定することです。アプリケーションで0.00000001未満の精度は必要ないことがわかっている場合、彼が提案することは、真実に非常に近いより正確な結果を得るのに十分です。最大精度を事前に知っているアプリケーションで役立ちます(たとえば、通貨精度の財務など)

ただし、それを四捨五入しようとする際の基本的な問題は、係数で除算して再スケーリングすると、実際には精度の問題の別の可能性が生じることです。ダブルスを操作すると、周波数が変化すると不正確な問題が発生する可能性があります。特に、非常に有効数字で丸めようとしている場合(つまり、オペランドが0未満である場合)、たとえば、Timonsコードで次を実行する場合:

System.out.println(round((1515476.0) * 0.00001) / 0.00001);

1499999.9999999998ここでの目標は、500000の単位で丸めることです(つまり、1500000が必要です)。

実際、不正確さを完全に排除する唯一の方法は、BigDecimalを使用してスケールオフすることです。例えば

System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());

イプシロン戦略とBigDecimal戦略を組み合わせて使用​​すると、精度を細かく制御できます。イプシロンであるという考えはあなたを非常に近づけ、BigDecimalはその後の再スケーリングによって引き起こされる不正確さを排除します。ただし、BigDecimalを使用すると、アプリケーションの期待されるパフォーマンスが低下します。

BigDecimalを使用して再スケーリングする最後のステップは、最終的な除算でエラーが再発生する可能性のある入力値がないと判断できる場合、一部のユースケースでは必ずしも必要ではないことが指摘されています。現在、これを適切に判断する方法がわからないので、誰かがその方法を知っていれば、それについて聞いて喜んでいます。

于 2012-04-05T12:50:42.127 に答える
3

この質問への回答を参照してください。基本的に、表示されているのは、浮動小数点演算を使用した自然な結果です。

任意の精度 (入力の有効桁数?) を選択して、結果を丸めることができます。

于 2008-10-07T17:02:42.350 に答える
1
double rounded = Math.rint(toround * 100) / 100;
于 2009-10-27T07:45:24.767 に答える
-1

正確な計算に double を使用するべきではありませんが、とにかく結果を丸める場合は、次のトリックが役に立ちました。

public static int round(Double i) {
    return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001));
}

例:

    Double foo = 0.0;
    for (int i = 1; i <= 150; i++) {
        foo += 0.00010;
    }
    System.out.println(foo);
    System.out.println(Math.round(foo * 100.0) / 100.0);
    System.out.println(round(foo*100.0) / 100.0);

どちらが印刷されますか:

0.014999999999999965
0.01
0.02

詳細: http://en.wikipedia.org/wiki/Double_precision

于 2011-11-30T13:35:35.117 に答える
-3

とても簡単です。

出力には %.2f 演算子を使用します。問題が解決しました!

例えば:

int a = 877.8499999999999;
System.out.printf("Formatted Output is: %.2f", a);

上記のコードの印刷出力は次のとおりです: 877.85

%.2f 演算子は、小数点以下 2 桁のみを使用する必要があることを定義します。

于 2012-03-11T15:07:26.783 に答える