私は常にお金やタイプを表現しないように言われてきました、そして今度はあなたに質問をします:なぜですか?double
float
私にはそれが何であるかがわからない、非常に正当な理由があると確信しています。
私は常にお金やタイプを表現しないように言われてきました、そして今度はあなたに質問をします:なぜですか?double
float
私にはそれが何であるかがわからない、非常に正当な理由があると確信しています。
回答:
floatとdoubleは、お金に使用する10の倍数を正確に表すことができないためです。この問題は、Javaだけでなく、base 2浮動小数点型を使用するプログラミング言語でも発生します。
基数10では、10.25を1025 * 10 -2(10の累乗の整数倍)と書くことができます。IEEE-754浮動小数点数は異なりますが、それらを考える非常に簡単な方法は、代わりに2の累乗を掛けることです。たとえば、164 * 2 -4(2 の累乗の整数倍)を見ると、これも10.25に等しくなります。これは、メモリ内での数値の表現方法ではありませんが、数学の意味は同じです。
基数10でも、この表記はほとんどの単純な分数を正確に表すことができません。たとえば、1/3を表すことはできません。10進表記は繰り返されているため(0.3333 ...)、10の累乗で乗算して1/3を得ることができる有限の整数はありません。3の長いシーケンスと333333333 * 10 -10のような小さな指数で解決できますが、正確ではありません。3を掛けると、1にはなりません。
ただし、お金を数える目的で、少なくとも米ドルが1ドルの桁で評価される国の場合、通常必要なのは10 -2の倍数を格納できることだけなので、実際には問題になりません。その1/3は表現できません。
floatとdoubleの問題は、お金のような数値の大部分が整数の2の累乗の正確な表現を持たないことです。実際、0と1の間の0.01の倍数のみ(これは、 IEEE 754の2進浮動小数点数として正確に表すことができる整数セントであるため、お金で)は0、0.25、0.5、0.75、1です。その他はすべて少しずれています。0.333333の例に例えると、0.1の浮動小数点値を取得して10倍すると、1は得られません。
ソフトウェアが小さなエラーを四捨五入するので、お金をdouble
またfloat
はとして表すとおそらく最初は見栄えがしますが、不正確な数値に対してさらに加算、減算、乗算、および除算を実行すると、エラーが複雑になり、目に見える値になります正確ではない。これは、浮動小数点数と倍精度浮動小数点数を、基本10の累乗の倍数の完全な精度が必要とされるお金を扱うには不十分です。
ほぼすべての言語で機能する解決策は、代わりに整数を使用してセントを数えることです。たとえば、1025は$ 10.25になります。いくつかの言語には、金銭に対処するための組み込み型もあります。特に、JavaにはBigDecimal
クラスがあり、C#にはdecimal
タイプがあります。
1.0 / 10 * 10
1.0と同じではない可能性があります。
Bloch、J.、Effective Java、第2版、アイテム48から:
float
そしてdouble
、0.1(又は10の任意の他の負のパワー)を表すことは不可能であるため、種類は特に金融計算のために不適当であるfloat
か、double
正確。たとえば、$ 1.03があり、42cを使っているとします。いくらお金が残っていますか。
System.out.println(1.03 - .42);
印刷し
0.6100000000000001
ます。この問題を解決する正しい方法は
BigDecimal
、int
またはを使用long
して金額を計算することです。
BigDecimal
ただし、いくつかの注意点があります(現在受け入れられている回答を参照してください)。
long a = 104
はドルの代わりにセントで使用して数えます。
BigDecimal
です。
これは正確さの問題ではなく、正確さの問題でもありません。それは、2の代わりに10を基に計算を行う人間の期待に応えることです。たとえば、財務計算にdoubleを使用しても、数学的な意味で「間違った」答えは生成されませんが、財政的な意味で期待されるものではありません。
出力の直前に結果を四捨五入しても、期待と一致しないdoubleを使用して結果が時々得られることがあります。
電卓を使用するか、手動で結果を計算すると、1.40 * 165 = 231になります。ただし、内部的にdoubleを使用しているため、私のコンパイラ/オペレーティングシステム環境では、230.99999に近い2進数として保存されます。そのため、数値を切り捨てると、231ではなく230になります。切り捨てではなく丸めを行うと、これは正しい結果ですが、丸めは常に切り捨てを伴います。どのような丸め手法を使用しても、切り上げが予想されるときに切り捨てられるこのような境界条件がまだあります。それらは非常にまれであるため、通常のテストや観察では発見されないことがよくあります。期待どおりに動作しない結果を示す例を検索するために、コードを記述する必要がある場合があります。
何かを最も近いペニーに四捨五入したいとします。したがって、最終結果を取得し、100を掛けて0.5を加え、切り捨ててから、結果を100で除算してペニーに戻します。保存した内部番号が3.465の代わりに3.46499999 ....だった場合、数値を最も近いペニーに丸めると、3.47ではなく3.46になります。しかし、ベース10の計算では、答えが正確に3.465である必要があることが示されている可能性があります。財務計算にdoubleを使用すると、このようなことが時々起こります。これはまれなので、問題として気付かれないことがよくありますが、それは起こります。
内部計算にdoubleではなくbase 10を使用する場合、コードに他のバグがないことを前提として、答えは常に人間が期待するものとまったく同じです。
Math.round(0.49999999999999994)
1を返すのですか?
私はこれらの応答のいくつかに困っています。倍精度浮動小数点数は、財務計算に影響があると思います。確かに、非小数の金額を加算および減算する場合、整数クラスまたはBigDecimalクラスを使用しても精度は失われません。しかし、より複雑な演算を実行すると、数値の格納方法に関係なく、多くの場合、結果は小数点以下の桁数が多くなります。問題は、結果をどのように提示するかです。
結果が切り上げと切り捨ての境界にあり、その最後のペニーが本当に重要である場合、おそらく小数点以下の桁数を増やして、答えがほぼ真ん中であることを視聴者に伝える必要があります。
doubleの問題、さらにはfloatの問題は、doubleを使用して大きな数と小さな数を組み合わせる場合です。Javaでは、
System.out.println(1000000.0f + 1.2f - 1000000.0f);
結果は
1.1875
floatとdoubleは概算です。BigDecimalを作成し、フロートをコンストラクターに渡すと、フロートが実際に何と等しいかがわかります。
groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375
これはおそらく$ 1.01を表現する方法ではありません。
問題は、IEEE仕様にすべての分数を正確に表す方法がないことです。一部の分数は繰り返し分数になり、近似エラーが発生します。会計士は物事が正確に1セントまで出てくることを好み、顧客が請求書を支払い、支払いが処理された後、.01を支払う必要があり、手数料を請求されるか、アカウントを閉鎖できないため、使用することをお勧めします。 10進数(C#)またはJavaのjava.math.BigDecimalのような正確な型。
丸めてもエラーを制御できないわけではありません。PeterLawreyによるこの記事を参照してください。そもそも丸める必要がないほうが簡単です。お金を扱うほとんどのアプリケーションは、多くの計算を必要としません。操作は、物を追加するか、異なるバケットに金額を割り当てることで構成されます。浮動小数点の導入と丸めは、物事を複雑にするだけです。
float
、double
およびBigDecimal
は正確な値を表します。コードからオブジェクトへの変換は、他の操作と同様に不正確です。タイプ自体は不正確ではありません。
私は反対票を投じられる危険を冒しますが、浮動小数点数が通貨計算に不適切であることは過大評価されていると思います。zneakによって説明される2進数の10進数表現の不一致に対抗するために、セントの丸めを正しく行い、処理するのに十分な有効桁数があることを確認する限り、問題は発生しません。
Excelで通貨を使用して計算する人は、常に倍精度浮動小数点数を使用しており(Excelには通貨タイプはありません)、丸めエラーについて不平を言う人はまだいません。
もちろん、あなたは理性の範囲内に留まる必要があります。たとえば、単純なWebショップではおそらく倍精度浮動小数点数で問題が発生することはないでしょうが、たとえば会計など、大量の(制限されていない)数値を追加する必要がある場合は、10フィートで浮動小数点数に触れたくないでしょう。ポール。
浮動小数点型は近似的に10進データしか表現できないことは事実ですが、数値を表示する前に必要な精度に丸めれば、正しい結果が得られることも事実です。通常。
通常、double型は精度が16桁未満であるためです。より高い精度が必要な場合は、適切なタイプではありません。また、近似値が累積する可能性があります。
固定小数点演算を使用する場合でも、数値を丸める必要がありますが、BigIntegerとBigDecimalが周期的な10進数を取得する場合にエラーを返すというのはそうではありませんでした。したがって、ここにも近似があります。
たとえば、歴史的に財務計算に使用されている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;
}
}
浮動小数点数の結果は正確ではないため、近似ではなく正確な結果を必要とする財務計算には適していません。floatとdoubleは工学および科学計算用に設計されており、正確な結果が得られない場合が多く、浮動小数点計算の結果はJVMによって異なる場合があります。金額の値を表すために使用されるBigDecimalおよびdoubleプリミティブの以下の例を見てください。浮動小数点の計算は正確ではない可能性があり、財務計算には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
double
FPをセントに使用しても、10進FPとは異なり、0.5セントまで計算しても問題はありません。浮動小数点の計算により、2進FPまたは10進FPを介して、たとえば123.499941¢の利息値が得られる場合、二重丸めの問題は同じです-どちらの方法にも利点はありません。あなたの前提は、数学的に正確な値と10進FPが同じであると想定しているようです。10進FPでも保証されません。0.5 / 7.0 * 7.0は、バイナリおよびデシマルFPの問題です。IAC、Cの次のバージョンが10進数のFPを提供することを期待しているので、ほとんどは無意味です。
先に述べたように、「ソフトウェアが小さなエラーを四捨五入するので、最初にダブルまたはフロートとしてお金を表すことはおそらく見栄えがよくなりますが、不正確な数値でより多くの加算、減算、乗算、および除算を実行すると、ますます精度が失われます。エラーが増えると、浮動小数点数と倍精度浮動小数点数は、基本10の累乗の倍数の完全な精度が必要とされる金銭を処理するには不十分になります。」
最後に、JavaにはCurrency And Moneyを操作する標準的な方法があります。
JSR 354:Money and Currency API
JSR 354は、MoneyとCurrencyを使用した包括的な計算を表現、転送、および実行するためのAPIを提供します。次のリンクからダウンロードできます。
JSR 354:Money and Currency APIダウンロード
仕様は次のもので構成されています。
- 金額や通貨などを処理するためのAPI
- 交換可能な実装をサポートするAPI
- 実装クラスのインスタンスを作成するためのファクトリー
- 金額の計算、変換、およびフォーマットの機能
- Money 9に含まれる予定のMoneyと通貨を操作するためのJava API。
- すべての仕様クラスとインターフェースは、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のコレクションを操作する場合、フィルタリング、並べ替え、およびグループ化に役立つユーティリティメソッドがいくつか利用できます。
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
リソース:
計算にさまざまなステップが含まれる場合、任意精度の計算では100%をカバーできません。
結果の完全な表現を使用する唯一の信頼できる方法(最後のステップに除算演算をバッチ処理するカスタムFractionデータ型を使用)および最後のステップでのみ10進表記に変換します。
小数点以下の桁数が多い数値や、次のような結果が常に存在する可能性があるため、任意の精度では効果が0.6666666
ありません。最後の例では、任意の表現ではカバーできません。そのため、各ステップで小さなエラーが発生します。
これらのエラーは追加され、最終的には無視できなくなる可能性があります。これはエラー伝播と呼ばれます。
ほとんどの回答は、お金と通貨の計算にダブルを使用してはならない理由を強調しています。そして私はそれらに完全に同意します。
ただし、その目的でdoubleを使用することはできません。
私はgc要件が非常に低い多くのプロジェクトに取り組んできましたが、BigDecimalオブジェクトがあることがそのオーバーヘッドの大きな原因でした。
この賢明な提案をもたらすのは、二重表現についての理解の欠如と、精度と精度を処理する経験の欠如です。
プロジェクトの精度と精度の要件を処理できる場合は、それを機能させることができます。これは、処理するdouble値の範囲に基づいて行う必要があります。
詳細については、グアバのFuzzyCompareメソッドを参照してください。パラメータの許容誤差が重要です。証券取引アプリケーションでこの問題に対処し、さまざまな範囲のさまざまな数値に使用する許容値について徹底的な調査を行いました。
また、実装であるハッシュマップを使用して、マップキーとしてDoubleラッパーを使用したい場合があります。Double.equalsとハッシュコードの例の値「0.5」と「0.6-0.1」は大きな混乱を引き起こすため、非常に危険です。
この質問に投稿された回答の多くは、IEEEと浮動小数点演算に関する標準について説明しています。
コンピューターサイエンス以外の背景(物理学と工学)の出身なので、問題を別の視点から見る傾向があります。私にとって、数学的計算でdoubleまたはfloatを使用しない理由は、情報を失いすぎるためです。
代替案は何ですか?たくさんあります(そして私が知らないもっとたくさんの!)。
JavaのBigDecimalは、Java言語にネイティブです。Apfloatは、Java用の別の任意精度ライブラリです。
C#のdecimalデータ型は、有効数字28桁のMicrosoftの.NET代替です。
SciPy(Scientific Python)は、おそらく財務計算も処理できます(私は試していませんが、そうだと思います)。
GNU Multiple Precision Library(GMP)とGNU MFPR Libraryは、CおよびC ++用の2つの無料のオープンソースリソースです。
JavaScript(!)用の数値精度ライブラリもあり、PHPは財務計算を処理できると思います。
また、多くのコンピューター言語向けのプロプライエタリ(特に、Fortran向け)とオープンソースのソリューションもあります。
私は訓練を受けたコンピュータ科学者ではありません。ただし、私はJavaのBigDecimalまたはC#のdecimalに傾く傾向があります。私はこれまでに挙げた他のソリューションを試していませんが、おそらく非常に良いソリューションでもあります。
私にとっては、サポートするメソッドのおかげでBigDecimalが好きです。C#の10進数はとてもいいですが、思い通りに操作する機会がありませんでした。余暇には興味のある科学計算を行っていますが、浮動小数点数の精度を設定できるため、BigDecimalは非常にうまく機能しているようです。BigDecimalの欠点?特に分割メソッドを使用している場合は、速度が遅くなることがあります。
速度を上げるために、C、C ++、およびFortranの無料の専用ライブラリを調べてみてください。
以前の回答を補足するために、BigDecimalのほかに、質問で取り上げられている問題を処理するときに、JavaでJoda-Moneyを実装するオプションもあります。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
いくつかの例...これはほとんどすべてのプログラミング言語で機能します(実際には期待どおりに機能しません)...私はDelphi、VBScript、Visual Basic、JavaScript、そして今やJava / Androidで試しました:
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!!!
Floatは、Decimalのバイナリ形式で、デザインが異なります。それらは2つの異なるものです。2つのタイプを相互に変換した場合、エラーはほとんどありません。また、floatは、科学的に無限大の値を表すように設計されています。つまり、その固定バイト数では、非常に小さい数から非常に大きい数に精度を失うように設計されています。Decimalは無限の数の値を表すことはできず、その数の10進数字に制限されます。したがって、FloatとDecimalは目的が異なります。
通貨価値のエラーを管理する方法はいくつかあります。
代わりに長整数を使用し、セントでカウントしてください。
倍精度を使用し、有効桁数を15のみに維持して、10進数を正確にシミュレーションできるようにします。値を表示する前に丸めます。計算を行うときに頻繁に丸めます。
Java BigDecimalなどの10進数ライブラリを使用して、10進数をシミュレートするためにdoubleを使用する必要がないようにします。
psほとんどのブランドのハンドヘルド関数電卓が浮動小数点数ではなく小数で動作することを知るのは興味深いことです。したがって、フロート変換エラーはありません。