Longを使用して通貨の独自のクラスを作成しようとしましたが、代わりにBigDecimal
を使用する必要があるようです。誰かが私を始めるのを手伝ってくれますか?ドル通貨にBigDecimal
sを使用する最良の方法は、少なくともセントの小数点以下2桁以下にするなどです。BigDecimal
のAPIは巨大です。使用する方法がわかりません。また、BigDecimal
の精度は向上していますが、double
を通過した場合、すべてが失われるわけではありませんか?新しいBigDecimal(24.99)
を実行すると、double
を使用した場合とどう違うのですか?または、代わりにString
を使用するコンストラクターを使用する必要がありますか?
以下にいくつかのヒントを示します。
BigDecimal
を使用します(お金の値はしばしばこれを必要とします)。NumberFormat
クラスを使用して表示します。このクラスは、異なる通貨の金額のローカライズの問題を処理します。ただし、プリミティブのみを取ります。したがって、double
への変換による精度のわずかな変化を受け入れることができる場合、このクラスを使用できます。NumberFormat
クラスを使用する場合、BigDecimal
インスタンスでscale()
メソッドを使用して、精度と丸め方法を設定します。PS:あなたが疑問に思っている場合、 BigDecimal
は、Javaで金額を表す必要がある場合、double
よりも常に優れています 。
PPS:
BigDecimal
インスタンスの作成
BigDecimal
はプリミティブ値を受け取るコンストラクタを提供する 、およびString
オブジェクトなので、これは非常に簡単です。これらを使用できます できればString
オブジェクトを取るもの 。例えば、
BigDecimal modelVal = new BigDecimal("24.455");
BigDecimal displayVal = modelVal.setScale(2, RoundingMode.HALF_EVEN);
BigDecimal
インスタンスの表示
setMinimumFractionDigits
およびsetMaximumFractionDigits
メソッド呼び出しを使用して、表示されるデータの量を制限できます。
NumberFormat usdCostFormat = NumberFormat.getCurrencyInstance(Locale.US);
usdCostFormat.setMinimumFractionDigits( 1 );
usdCostFormat.setMaximumFractionDigits( 2 );
System.out.println( usdCostFormat.format(displayVal.doubleValue()) );
Money Patternについて少し研究することをお勧めします。 Martin Fowlerは著書のAnalysis patternでこれをより詳細に説明しています。
public class Money {
private static final Currency USD = Currency.getInstance("USD");
private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN;
private final BigDecimal amount;
private final Currency currency;
public static Money dollars(BigDecimal amount) {
return new Money(amount, USD);
}
Money(BigDecimal amount, Currency currency) {
this(amount, currency, DEFAULT_ROUNDING);
}
Money(BigDecimal amount, Currency currency, RoundingMode rounding) {
this.currency = currency;
this.amount = amount.setScale(currency.getDefaultFractionDigits(), rounding);
}
public BigDecimal getAmount() {
return amount;
}
public Currency getCurrency() {
return currency;
}
@Override
public String toString() {
return getCurrency().getSymbol() + " " + getAmount();
}
public String toString(Locale locale) {
return getCurrency().getSymbol(locale) + " " + getAmount();
}
}
使用法について:
Money
ではなくBigDecimal
オブジェクトを使用して、すべての資金を表します。お金を大きな小数で表すと、表示するすべての場所でお金をフォーマットする必要があります。ディスプレイの標準が変わるかどうかを想像してください。あらゆる場所で編集を行う必要があります。代わりにMoney
パターンを使用して、お金のフォーマットを単一の場所に集中化します。
Money price = Money.dollars(38.28);
System.out.println(price);
または、 JSR-354 を待ちます。 Java Money and Currency APIは近日公開予定!
1)double
精度に制限されている場合、BigDecimal
sを使用する理由の1つは、BigDecimal
sから作成されたdouble
sを使用した操作を実現することです。
2)BigDecimal
は、任意精度の整数スケールなし値と非負の32ビット整数スケールで構成され、doubleはプリミティブ型double
の値をオブジェクトにラップします。タイプDouble
のオブジェクトには、タイプがdouble
である単一のフィールドが含まれます
3)違いはありません
$と精度に問題はないはずです。それを行う1つの方法は、System.out.printf
セントを小数点以下2桁に切り上げる場合は、BigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP)
を使用します。ただし、計算を行うときは、四捨五入のエラーに注意してください。お金の価値の丸めを行うときは、一貫性が必要です。すべての計算が行われた後、最後に一度だけ丸めを行うか、計算を行う前に各値に丸めを適用します。どちらを使用するかは、ビジネス要件によって異なりますが、一般的に、最後に丸めを行うことは私にとって理にかなっていると思います。
お金の価値のためにString
を構築するとき、BigDecimal
を使用します。 double
を使用すると、末尾に浮動小数点値が末尾に付きます。これは、double
/float
値がバイナリ形式でどのように表されるかに関するコンピューターアーキテクチャによるものです。
プリミティブ数値型は、単一の値をメモリに保存するのに役立ちます。ただし、double型とfloat型を使用した計算を扱う場合、丸めに問題があります。メモリ表現が値に正確にマッピングされないために発生します。たとえば、double値は64ビットを取ると想定されていますが、Javaは64ビットすべてを使用するわけではありません。 float型またはdouble型の値を一緒に追加すると、間違った値になります。
短いクリップをご覧ください https://youtu.be/EXxUSz9x7BM
NumberFormat.getNumberInstance(Java.util.Locale.US).format(num);
私は過激になります。 BigDecimalなし
ここに素晴らしい記事があります https://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money/
ここからのアイデア。
_import Java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
testConstructors();
testEqualsAndCompare();
testArithmetic();
}
private static void testEqualsAndCompare() {
final BigDecimal zero = new BigDecimal("0.0");
final BigDecimal zerozero = new BigDecimal("0.00");
boolean zerosAreEqual = zero.equals(zerozero);
boolean zerosAreEqual2 = zerozero.equals(zero);
System.out.println("zerosAreEqual: " + zerosAreEqual + " " + zerosAreEqual2);
int zerosCompare = zero.compareTo(zerozero);
int zerosCompare2 = zerozero.compareTo(zero);
System.out.println("zerosCompare: " + zerosCompare + " " + zerosCompare2);
}
private static void testArithmetic() {
try {
BigDecimal value = new BigDecimal(1);
value = value.divide(new BigDecimal(3));
System.out.println(value);
} catch (ArithmeticException e) {
System.out.println("Failed to devide. " + e.getMessage());
}
}
private static void testConstructors() {
double doubleValue = 35.7;
BigDecimal fromDouble = new BigDecimal(doubleValue);
BigDecimal fromString = new BigDecimal("35.7");
boolean decimalsEqual = fromDouble.equals(fromString);
boolean decimalsEqual2 = fromString.equals(fromDouble);
System.out.println("From double: " + fromDouble);
System.out.println("decimalsEqual: " + decimalsEqual + " " + decimalsEqual2);
}
}
_
印刷する
_From double: 35.7000000000000028421709430404007434844970703125
decimalsEqual: false false
zerosAreEqual: false false
zerosCompare: 0 0
Failed to devide. Non-terminating decimal expansion; no exact representable decimal result.
_
BigDecimalをデータベースに保存するのはどうですか?地獄、それはまた、二重値として格納します???少なくとも、高度な設定なしでmongoDbを使用すると、_BigDecimal.TEN
_を_1E1
_として保存します。
付属しています-Stringを使用してBigDecimalをJavaとしてStringとしてデータベースに格納します。検証があります。例えば_@NotNull
_、@Min(10)
など...その後、更新または保存のトリガーを使用して、現在の文字列が必要な数であるかどうかを確認できます。 Mongodbトリガー関数呼び出しの組み込み方法はありますか?
私が楽しんでいる欠点が1つあります- Swagger defenitionの文字列としてのBigDecimal
Swaggerを生成する必要があるため、フロントエンドチームは、文字列として表示される数字を渡すことを理解しています。 DateTimeたとえば、文字列として表示されます。
上記の記事で読んだ別のクールなソリューションがあります...longを使用して正確な数値を保存します。
標準のlong値は、米国国債の現在の値(ドルではなくセント)を6477回オーバーフローなしで保存できます。さらに、浮動小数点ではなく整数型です。これにより、作業が簡単かつ正確になり、動作が保証されます。
https://stackoverflow.com/a/27978223/4587961
将来的には、MongoDbがBigDecimalのサポートを追加するかもしれません。 https://jira.mongodb.org/browse/SERVER-139 3.3.8はこれを完了したようです。
これは、2番目のアプローチの例です。スケーリングを使用します。 http://www.technology-ebay.de/the-teams/mobile-de/blog/mapping-bigdecimals-with-morphia-for-mongodb.html