web-dev-qa-db-ja.com

通貨を表すのにDoubleやFloatを使わないのはなぜですか?

私はいつも never doubleまたはfloatタイプでお金を表すように言われました、そして今度は私があなたに質問を投げかけます:なぜ?

私は非常に正当な理由があると確信しています、私は単にそれが何であるかわからない。

840

浮動小数点数と倍精度数は、お金で使う10の倍数を正確に表すことができないためです。この問題はJavaだけの問題ではなく、基数2の浮動小数点型を使用するすべてのプログラミング言語の問題です。

基数10では、10.25と1025と書くことができます* 10-2 (整数倍の10のべき乗) IEEE-754浮動小数点数 は異なりますが、それらについて考えるための非常に単純な方法は、代わりに2のべき乗を掛けることです。例えば、あなたは164 * 2を見ているでしょう。-4 (整数倍の2のべき乗)、これも10.25に等しい。それは数値がメモリでどのように表現されるかではありませんが、数学的な意味は同じです。

基数10でも、この表記法は最も単純な分数を正確に表すことはできません。たとえば、1/3を表現することはできません。小数表現は繰り返し(0.3333 ...)です。したがって、1/3を得るために10のべき乗を掛けることができる有限の整数はありません。 333333333 * 10のように、3の長いシーケンスと小さい指数に決着することができます。-10しかし、それは正確ではありません:あなたがそれを3倍にしても、1は得られません。

ただし、お金を数えるためには、少なくとも米ドルの桁の範囲内でお金が評価される国では、通常必要なのは10の倍数を格納できることだけです。-2したがって、1/3が表現できないことは実際問題ではありません。

浮動小数点数と倍精度数の問題は、 大多数 のようなお金の数の整数倍の2のべき乗として正確な表現を持たないことです。実際には、0と1の間の0.01の倍数IEEE-754 2進浮動小数点数として正確に表すことができる(それはそれらが整数セントであるのでお金を扱うとき重要です)他はすべて少しだけオフになっています。 0.333333の例と同様に、0.1の浮動小数点数を10に掛けると1は得られません。

お金をdoubleまたはfloatとして表すと、最初はソフトウェアで小さなエラーが四捨五入されるので見栄えがよくなりますが、不正確な数に対して加算、減算、乗算、除算を行うと、エラーが増え、最終的に明らかに正確ではありません。これは浮動小数点数を倍増させ、基数10のべき乗の倍数に対する完全な正確さが要求されるところでお金を扱うには不適切になります。

ほぼすべての言語で機能する解決策は、代わりに整数を使用し、セントを数えることです。たとえば、1025は10.25ドルになります。いくつかの言語はお金を扱うための組み込み型も持っています。とりわけ、Javaには BigDecimal クラスがあり、C#には decimal タイプがあります。

881
zneak

Bloch、J.、Effective Java、第2版、Item 48から:

floatおよびdouble型は、0.1(またはその他の10の負のべき乗)を正確にfloatまたはdoubleとして表すことは不可能であるため、通貨計算には特に適していません。

例えば、あなたが1.03ドルを持っていて、42cを使ったとしましょう。あなたはいくらお金を残しましたか?

System.out.println(1.03 - .42);

0.6100000000000001を印刷します。

この問題を解決する正しい方法は、金銭計算にBigDecimalint、またはlongを使用することです。

BigDecimalには注意点がいくつかあります(現在受け入れられている回答をご覧ください)。

287
dogbane

これは正確さの問題ではなく、また正確さの問題でもありません。それは、基数2ではなく基数10を計算に使用する人間の期待に応えることです。たとえば、財務計算に倍精度を使用しても、数学的な意味で「間違った」答えは得られません。経済的な意味で予想されるものではありません。

出力前の最後の1分で結果を四捨五入しても、予想と一致しない倍精度を使用して結果を得ることがあります。

計算機を使用するか、手動で結果を計算すると、正確に1.40 * 165 = 231となります。ただし、私のコンパイラ/オペレーティングシステム環境では、doubleを内部的に使用すると、230.99999に近い2進数として格納されます。したがって、数値を切り捨てると、231ではなく230が得られます。それは本当です、しかし丸めは常に切り捨てを含みます。どのような丸め手法を使用しても、切り上げられると予想されるときに切り捨てられるこのような境界条件がまだあります。それらは偶然のテストまたは観察を通してしばしば見つけられないであろうというそれらは十分にまれです。予想どおりに動作しない結果を示す例を検索するためのコードを書く必要があるかもしれません。

あなたが最も近いペニーに何かを丸めたいと仮定します。だからあなたはあなたの最終結果を取り、100を掛け、0.5を加え、切り捨て、そして結果を100で割ってペニーに戻る。あなたが保存した内部番号が3.465の代わりに3.46499999 ....だったならば、あなたが最も近いペニーに数を丸めるとき、あなたは3.47の代わりに3.47を得ようとしています。しかし、あなたの10進法の計算は、答えが正確に3.465であるべきであることを示しているかもしれず、それは明らかに3.46にではなく3.47に丸めるべきである。あなたが財務計算のために倍精度を使用するとき、この種のことは実生活で時折起こります。めったに起こらないので、問題として気付かれないことがよくありますが、起こります。

あなたがあなたのコードの他のバグがないと仮定すると、あなたがあなたの内部計算のために倍精度の代わりに基数10を使うならば、答えは常にまさに人間によって予想されるものです。

68

私はこれらの反応のいくつかに悩んでいます。ダブルスやフロートが財務計算に位置を占めると思います。確かに、非小計金額を加算および減算するときに、整数クラスまたはBigDecimalクラスを使用するときに精度が低下することはありません。しかし、もっと複雑な演算を実行すると、数値の格納方法に関係なく、結果が小数点以下の桁数になってしまうことがよくあります。問題はどうやって結果を提示するかです。

あなたの結果が切り上げと切り捨ての境界にあり、そして最後のペニーが本当に重要であるならば、あなたはおそらく答えがほぼ中央にあることを視聴者に伝えるべきです - より多くの小数位を表示することによって。

倍精度浮動小数点数の問題、そして浮動小数点数の問題は、大きい数と小さい数の組み合わせに使用される場合です。 Javaでは、

System.out.println(1000000.0f + 1.2f - 1000000.0f);

になります

1.1875
46
Rob Scala

フロートとダブルは概算です。 BigDecimalを作成してfloatをコンストラクタに渡すと、floatが実際に何に等しいのかがわかります。

groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375

これはおそらくあなたが$ 1.01を表現したい方法ではありません。

問題は、IEEEの仕様ではすべての分数を正確に表現する方法がなく、それらの一部は繰り返し分数となるため、近似誤差が生じることです。会計士は正確にペニーに出てくることを好むので、彼らは彼らの請求書を支払うと、支払いが処理された後彼らは0.01を負うし、彼らは手数料を支払うか、自分のアカウントを閉じることができない10進数(C#では)またはJavaのJava.math.BigDecimalのような正確な型。

丸めてもエラーが制御できないわけではありません: Peter Lawreyによるこの記事を参照してください 。そもそも四捨五入する必要がないほうが簡単です。お金を扱うほとんどのアプリケーションは多くの数学を必要としません、操作は物事を加えるか、または異なるバケツに金額を割り当てることから成ります。浮動小数点の導入と丸めは物事を複雑にするだけです。

36
Nathan Hughes

私は控えめにされる危険性があるでしょう、しかし私は通貨計算のための浮動小数点数の不適当さが過大評価されると思います。 zneakで説明されている2進数と10進数の表現の不一致に対処するために、セント四捨五入が正しく行われ、有効な有効数字が十分にある限り、問題は生じません。

Excelで通貨を使って計算する人は常に倍精度浮動小数点数を使っていました(Excelには通貨タイプはありません)。丸め誤差について不満を言う人はまだいません。

もちろん、あなたは理にかなっていなければなりません。例えば単純なWebショップでは、倍精度浮動小数点数ではおそらく問題は発生しません。会計処理など、大量の(制限のない)数値を追加する必要がある場合は、浮動小数点数に10フィートの極を付けたくはありません。

18
escitalopram

浮動小数点型は近似的に10進数のデータしか表現できないのは事実ですが、数値を表示する前に必要な精度に丸めると、正しい結果が得られることも事実です。通常。

通常、double型の精度は16桁未満です。あなたがより良い精度を必要とするならば、それは適切なタイプではありません。近似も累積することがあります。

たとえあなたが固定小数点演算を使ったとしても、あなたはまだ数を丸める必要があるということは言われなければならない、それはあなたが周期的な10進数を得るならBigIntegerとBigDecimalがエラーを与えるという事実のためではなかった。だからここでも近似があります。

たとえば、これまで財務計算に使用されてきたCOBOLの最大精度は18桁です。そのため、暗黙的な丸めがよくあります。

結論として、私の考えでは、倍精度は概してその16桁の精度には適していません。

後続のプログラムの次の出力を検討してください。これは、doubleを四捨五入した後、精度16までBigDecimalと同じ結果になることを示しています。

Precision 14
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.000051110111115611
Double                        : 56789.012345 / 1111111111 = 0.000051110111115611

Precision 15
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.0000511101111156110
Double                        : 56789.012345 / 1111111111 = 0.0000511101111156110

Precision 16
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.00005111011111561101
Double                        : 56789.012345 / 1111111111 = 0.00005111011111561101

Precision 17
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.000051110111115611011
Double                        : 56789.012345 / 1111111111 = 0.000051110111115611013

Precision 18
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.0000511101111156110111
Double                        : 56789.012345 / 1111111111 = 0.0000511101111156110125

Precision 19
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.00005111011111561101111
Double                        : 56789.012345 / 1111111111 = 0.00005111011111561101252

import Java.lang.reflect.InvocationTargetException;
import Java.lang.reflect.Method;
import Java.math.BigDecimal;
import Java.math.MathContext;

public class Exercise {
    public static void main(String[] args) throws IllegalArgumentException,
            SecurityException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException {
        String amount = "56789.012345";
        String quantity = "1111111111";
        int [] precisions = new int [] {14, 15, 16, 17, 18, 19};
        for (int i = 0; i < precisions.length; i++) {
            int precision = precisions[i];
            System.out.println(String.format("Precision %d", precision));
            System.out.println("------------------------------------------------------");
            execute("BigDecimalNoRound", amount, quantity, precision);
            execute("DoubleNoRound", amount, quantity, precision);
            execute("BigDecimal", amount, quantity, precision);
            execute("Double", amount, quantity, precision);
            System.out.println();
        }
    }

    private static void execute(String test, String amount, String quantity,
            int precision) throws IllegalArgumentException, SecurityException,
            IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
        Method impl = Exercise.class.getMethod("divideUsing" + test, String.class,
                String.class, int.class);
        String price;
        try {
            price = (String) impl.invoke(null, amount, quantity, precision);
        } catch (InvocationTargetException e) {
            price = e.getTargetException().getMessage();
        }
        System.out.println(String.format("%-30s: %s / %s = %s", test, amount,
                quantity, price));
    }

    public static String divideUsingDoubleNoRound(String amount,
            String quantity, int precision) {
        // acceptance
        double amount0 = Double.parseDouble(amount);
        double quantity0 = Double.parseDouble(quantity);

        //calculation
        double price0 = amount0 / quantity0;

        // presentation
        String price = Double.toString(price0);
        return price;
    }

    public static String divideUsingDouble(String amount, String quantity,
            int precision) {
        // acceptance
        double amount0 = Double.parseDouble(amount);
        double quantity0 = Double.parseDouble(quantity);

        //calculation
        double price0 = amount0 / quantity0;

        // presentation
        MathContext precision0 = new MathContext(precision);
        String price = new BigDecimal(price0, precision0)
                .toString();
        return price;
    }

    public static String divideUsingBigDecimal(String amount, String quantity,
            int precision) {
        // acceptance
        BigDecimal amount0 = new BigDecimal(amount);
        BigDecimal quantity0 = new BigDecimal(quantity);
        MathContext precision0 = new MathContext(precision);

        //calculation
        BigDecimal price0 = amount0.divide(quantity0, precision0);

        // presentation
        String price = price0.toString();
        return price;
    }

    public static String divideUsingBigDecimalNoRound(String amount, String quantity,
            int precision) {
        // acceptance
        BigDecimal amount0 = new BigDecimal(amount);
        BigDecimal quantity0 = new BigDecimal(quantity);

        //calculation
        BigDecimal price0 = amount0.divide(quantity0);

        // presentation
        String price = price0.toString();
        return price;
    }
}
17
user1593165

浮動小数点数の結果は正確ではないため、正確な結果を必要とし、近似を必要としない財務計算には適していません。 floatとdoubleは工学的および科学的計算用に設計されており、浮動小数点計算の結果もJVMによって異なる場合がありますが、正確な結果は得られません。以下のBigDecimalと貨幣価値を表すのに使われるdouble primitiveの例を見てください。浮動小数点計算は正確ではないかもしれないことは明らかで、金融計算にBigDecimalを使うべきです。

    // floating point calculation
    final double amount1 = 2.0;
    final double amount2 = 1.1;
    System.out.println("difference between 2.0 and 1.1 using double is: " + (amount1 - amount2));

    // Use BigDecimal for financial calculation
    final BigDecimal amount3 = new BigDecimal("2.0");
    final BigDecimal amount4 = new BigDecimal("1.1");
    System.out.println("difference between 2.0 and 1.1 using BigDecimal is: " + (amount3.subtract(amount4)));

出力:

difference between 2.0 and 1.1 using double is: 0.8999999999999999
difference between 2.0 and 1.1 using BigDecimal is: 0.9
16
Dheeraj Arora

先に述べたように、「ダブルまたはフロートとしてお金を表すことは、ソフトウェアが小さなエラーを四捨五入するとき最初はおそらく良く見えるでしょうが、不正確な数に対してより多くの足し算、引き算、掛け算および割り算を実行するにつれてこれにより、浮動小数点数が倍増し、基数10のべき乗の倍数の完全な正確さが要求される場合には、お金を扱うには不十分になります。」

最後に、Javaには通貨とお金を扱うための標準的な方法があります。

JSR 354:お金と通貨のAPI

JSR 354は、MoneyとCurrencyを使用して表現、転送、および包括的な計算を実行するためのAPIを提供します。このリンクからダウンロードできます。

JSR 354:Money and Currency APIダウンロード /

仕様は次のもので構成されています。

  1. Eを処理するためのAPI g。金額と通貨
  2. 互換性のある実装をサポートするためのAPI
  3. 実装クラスのインスタンスを作成するためのファクトリ
  4. 金額の計算、変換、および書式設定の機能
  5. Java 9に含まれる予定のMoney and Currenciesと連携するためのJava API。
  6. すべての仕様クラスとインタフェースは、javax.money。*パッケージにあります。

JSR 354のサンプル例:Money and Currency API:

MonetaryAmountを作成してそれをコンソールに出力する例は、次のようになります。

MonetaryAmountFactory<?> amountFactory = Monetary.getDefaultAmountFactory();
MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

参照実装APIを使用する場合、必要なコードははるかに簡単です。

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

APIはMonetaryAmountsによる計算もサポートしています。

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));

CurrencyUnitとMonetaryAmount

// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);

MonetaryAmountには、割り当てられた通貨、数値、その精度などにアクセスするためのさまざまなメソッドがあります。

MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();

int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5

// NumberValue extends Java.lang.Number. 
// So we assign numberValue to a variable of type Number
Number number = numberValue;

MonetaryAmountsは、丸め演算子を使用して丸めることができます。

CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35

MonetaryAmountsのコレクションを操作するときは、フィルタリング、ソート、およびグループ化のためのいくつかのNiceユーティリティメソッドを使用できます。

List<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));

カスタムMonetaryAmountオペレーション

// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {
  BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);
  BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));
  return Money.of(tenPercent, amount.getCurrency());
};

MonetaryAmount dollars = Money.of(12.34567, "USD");

// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567

リソース:

JSR 354によるJavaのお金と通貨の取り扱い

Java 9 Money and Currency API(JSR 354)を調べる

参照: JSR 354 - 通貨とお金

7
Virtual

あなたの計算が様々なステップを含む場合、任意精度演算はあなたを100%カバーしないでしょう。

結果の完全な表現を使用する唯一の信頼できる方法(最後のステップへの除算演算をバッチ処理するカスタムの分数データ型を使用)と最後のステップでは10進表記にのみ変換する。

小数点以下の桁数が多い数字や0.6666666のような結果がある可能性があるので、任意の精度では役に立たないでしょう。最後の例では、任意の表現は含まれません。だからあなたは各ステップで小さなエラーがあるでしょう。

このエラーは累積し、最終的にはもう無視するのが容易ではなくなるかもしれません。これは エラー伝播 と呼ばれます。

4
Kemal Dağ

ほとんどの回答は、お金と通貨の計算に倍精度を使用してはいけない理由を強調しています。そして私は彼らに完全に同意します。

ただし、doubleがその目的に使用されることは決してないという意味ではありません。

私は非常に低いgc要件を持ついくつかのプロジェクトに取り組んできました、そしてBigDecimalオブジェクトを持つことはそのオーバーヘッドの大きな一因でした。

この賢明な提案をもたらすのは、二重表現についての理解の欠如と正確さと正確さの取り扱いにおける経験の欠如です。

プロジェクトの精度と精度の要件を処理できる場合は、それを機能させることができます。これは、どのdouble値の範囲を扱うかに基づいて実行する必要があります。

あなたはより多くのアイデアを得るためにguavaのFuzzyCompareメソッドを参照することができます。パラメータの許容誤差が重要です。証券取引アプリケーションのためにこの問題に対処し、異なる範囲の異なる数値に対してどの許容値を使用するかについて徹底的な調査を行いました。

また、ハッシュマップを実装として、マップキーとしてDoubleラッパーを使用したい場合があるかもしれません。 Double.equalsとハッシュコードの例の値 "0.5"と "0.6 - 0.1"は大きな混乱を招くため、非常に危険です。

2
Dev Amitabh

通貨の表現にはIntegerかLongを使うのが好きです。 BigDecimalはソースコードをやり過ぎます。

あなたはただあなたのすべての価値がセントであることを知っていなければなりません。またはあなたが使っている通貨の中で最も低い値。

1
Tony Ennis

この質問に投稿された回答の多くは、IEEEと浮動小数点演算を取り巻く標準について説明しています。

非コンピュータ科学の背景(物理学と工学)から来て、私は別の観点から問題を見る傾向があります。私にとって、数学の計算でdoubleやfloatを使わないのは、情報を失いすぎるからです。

代替案は何ですか?たくさんあります(そして私は知らないがもっとたくさんあります)。

JavaのBigDecimalはJava言語にネイティブです。 Apfloatは、Java用のもう1つの任意精度ライブラリです。

C#の10進データ型は、28の有効数字のためのMicrosoftの.NET代替手段です。

SciPy(Scientific Python)もおそらく財務計算を扱うことができます(私は試したことはありませんが、そう思うのです)。

GNU多重精度ライブラリ(GMP)とGNU MFPRライブラリは、CとC++用の2つのフリーでオープンソースのリソースです。

JavaScript用の数値精度ライブラリ(!)もあり、私はPHPが財務計算を扱うことができると思います。

独自の(特にFortranにとっては)私が思うに、多くのコンピュータ言語用のオープンソースソリューションもあります。

私は訓練を受けたコンピューター科学者ではありません。しかし、私はJavaのBigDecimalかC#のdecimalのどちらかに傾く傾向があります。私がリストした他の解決策を試したことはありませんが、それらもおそらく非常に優れています。

私にとっては、BigDecimalがサポートしているメソッドのために私はBigDecimalが好きです。 C#の小数は非常にいいですが、私が望むほど多くはそれを使用する機会がありませんでした。私は余暇に私が興味を持って科学的な計算をします、そしてBigDecimalは私の浮動小数点数の精度を設定できるのでとてもうまくいくようです。 BigDecimalの欠点は?特にdivideメソッドを使用している場合は、時間がかかることがあります。

あなたは、スピードを上げるために、C、C++、そしてFortranの中の自由で所有権のあるライブラリを調べるかもしれません。

1
fishermanhat

これまでの答えを補足するために、BigDecimalの他に、質問で扱われている問題に対処するときに、 Joda-Money をJavaで実装するオプションもあります。 Javaモジュール名はorg.joda.moneyです。

Java SE 8以降が必要で、依存関係はありません。

より正確には、コンパイル時の依存関係がありますが、必須ではありません。

<dependency>
  <groupId>org.joda</groupId>
  <artifactId>joda-money</artifactId>
  <version>1.0.1</version>
</dependency>

Joda Moneyの使用例

  // create a monetary value
  Money money = Money.parse("USD 23.87");

  // add another amount with safe double conversion
  CurrencyUnit usd = CurrencyUnit.of("USD");
  money = money.plus(Money.of(usd, 12.43d));

  // subtracts an amount in dollars
  money = money.minusMajor(2);

  // multiplies by 3.5 with rounding
  money = money.multipliedBy(3.5d, RoundingMode.DOWN);

  // compare two amounts
  boolean bigAmount = money.isGreaterThan(dailyWage);

  // convert to GBP using a supplied rate
  BigDecimal conversionRate = ...;  // obtained from code outside Joda-Money
  Money moneyGBP = money.convertedTo(CurrencyUnit.GBP, conversionRate, RoundingMode.HALF_UP);

  // use a BigMoney for more complex calculations where scale matters
  BigMoney moneyCalc = money.toBigMoney();

ドキュメント: http://joda-money.sourceforge.net/apidocs/org/joda/money/Money.html

実装例: https://www.programcreek.com/Java-api-examples/?api=org.joda.money.Money

1
Tadija Malić

いくつかの例...これは(実際には期待どおりには動作しません)、ほとんどすべてのプログラミング言語で動作します。

    double total = 0.0;

    // do 10 adds of 10 cents
    for (int i = 0; i < 10; i++) {
        total += 0.1;  // adds 10 cents
    }

    Log.d("round problems?", "current total: " + total);

    // looks like total equals to 1.0, don't?

    // now, do reverse
    for (int i = 0; i < 10; i++) {
        total -= 0.1;  // removes 10 cents
    }

    // looks like total equals to 0.0, don't?
    Log.d("round problems?", "current total: " + total);
    if (total == 0.0) {
        Log.d("round problems?", "is total equal to ZERO? YES, of course!!");
    } else {
        Log.d("round problems?", "is total equal to ZERO? NO... thats why you should not use Double for some math!!!");
    }

出力:

round problems?: current total: 0.9999999999999999 round problems?: current total: 2.7755575615628914E-17 round problems?: is total equal to ZERO? NO... thats why you should not use Double for some math!!!

1
WilliamK