質問

この質問にはすでに答えがあります:

減算によって何らかの問題が発生し、結果の値が間違っているようです。

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

78.75 = 787.5 * 10.0/100日

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 になります。

正しい計算を保証するにはどうすればよいですか?

役に立ちましたか?

解決

浮動小数点演算の精度を制御するには、 java.math.BigDecimal 。詳細については、John Zukowskiの 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

他のヒント

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

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

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

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

これは、これらの微妙な丸めエラーが発生した場合に重要になります。数値は.585と表示される場合がありますが、内部的にはその値は「0.58499999999999996447286321199499070644378662109375」です。 <=>コンストラクタを使用した場合、0.585に等しくない数値を取得しますが、静的メソッドは0.585に等しい値を提供します。

double value = 0.585;
System.out.println(new BigDecimal(value));
System.out.println(BigDecimal.valueOf(value));

私のシステムでは

0.58499999999999996447286321199499070644378662109375
0.585

別の例:

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は、少なくともこの点で一貫した動作を保証します。

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

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では、数字は文字列としてとにかく始まります。

doubleを使用して計算を行うと、いつでもこれが起こります。このコードは877.85を提供します:

double answer = Math.round(dCommission * 100000)/ 100000.0;

ドルではなくセントの数を保存し、出力時にフォーマットをドルに変換します。そうすれば、精度の問題の影響を受けない整数を使用できます。

この質問への回答をご覧ください。基本的に、あなたが見ているのは、浮動小数点演算を使用した自然な結果です。

任意の精度(入力の有効桁数)を選択し、それを快適に実行できる場合は、結果をそれに丸めることができます。

これは楽しい問題です。

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

ただし、四捨五入しようとする根本的な問題は、係数で割ってスケールを変更すると、実際には精度の問題が別の可能性を引き起こすことです。double を操作すると、さまざまな頻度で不精度の問題が発生する可能性があります。特に、非常に有効な桁で丸めようとしている場合 (つまり、オペランドが 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 を使用して再スケーリングする最後のステップは、最後の除算でエラーが再導入される可能性のある入力値がないと判断できるユースケースによっては、必ずしも必要ではないことが指摘されています。現時点ではこれを適切に判断する方法がわからないので、誰かがその方法を知っている場合は、それについてお知らせいただけると幸いです。

これまで、Javaでこれを行う最もエレガントで効率的な方法:

double newNum = Math.floor(num * 100 + 0.5) / 100;

さらに良いのは、 JScience をBigDecimalがかなり制限されているためです(たとえば、sqrt関数)

double dCommission = 1586.6 - 708.75;
System.out.println(dCommission);
> 877.8499999999999

Real dCommissionR = Real.valueOf(1586.6 - 708.75);
System.out.println(dCommissionR);
> 877.850000000000
double rounded = Math.rint(toround * 100) / 100;

正確な計算には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

非常に簡単です。

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

例:

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

上記のコードは、次の印刷出力になります。 877.85

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top