Javaの金額計算で学んだこと
前置き
プロジェクトで使っているのはJava8です。
事の発端
Javaで金額計算が絡むコーディングをしていて、画面に表示される値が
299.999999999999988897769753748434595763683319091796875000
のように微妙にずれる事象に遭遇しました。
恥ずかしながら、ちゃんと(?)金額を計算するようなロジックを初めて書いたので、
なぜそうなるのかといった理由も調べながら進めました。
犯人探し
金額に対してパーセンテージを掛けるような処理の前段で
// どこかのmodelから取得したパーセンテージと金額
double d = 0.3;
BigDecimal bd = new BigDecimal("1000");
// 計算
BigDecimal bd2 = bd.multiply(new BigDecimal(d));
System.out.println(bd2); // 299.999999999999988897769753748434595763683319091796875000
doubleの引数をコンストラクタに渡してBigDecimalインスタンスを生成していたんですね。
DBからごにょごにょ取得した結果、いろいろあってServiveクラスで使用しているmodelのフィールドがdouble型で定義されていました。
なんでずれるの?
コード上は10進数で定義していますが、内部では2進数で扱っているため、
2進数で循環小数になるような値(例えばdoubleの0.3)は計算をすると丸めが発生、つまり誤差が発生します。
こういうのもだめ
double d = 3.3 / 1.1 // 3になってほしいけど2.9999999999999996
ここでBigDecimalさんの使い方を再確認(汗)
double d = 3.3 / 1.1 // 3になってほしいけど2.9999999999999996
とりあえずリファレンスを読んでみました。
https://docs.oracle.com/javase/jp/8/docs/api/java/math/BigDecimal.html
- add
- subtract
- multiply
- divide
などのメソッドを用いて四則演算します。
RoundingModeで四捨五入/切り捨て/切り上げの指定をします。
今回のロジックはそこまで複雑じゃなかったのでややこしいメソッドは使わなかったです。
だめなのと良いの
// 毎回インスタンス生成、効率が悪い
BigDecimal b1 = new BigDecimal(10); //だめ
// キャッシュを使うvalueOf()
BigDecimal b2 = BigDecimal.valueOf(10); // 良い
// ∵2進数で循環小数になるものは誤差が発生する
BigDecimal b3 = new BigDecimal(0.3); // だめ
// 文字列から生成する
BigDecimal b4 = new BigDecimal("0.3"); // 良い
// doubleの値をBigDecimalに変換したいとき
BigDecimal b5 = BigDecimal.valueOf(0.3); // (なるべくならこの変換が発生する実装にはしたくないけど)
- BigDecimal#ROUND_xxxx系はJava9以降でDeprecatedです。RoundingModeを使いましょう(今のプロジェクトではJava8を使っていますが、バージョンアップを見据えてそのあたりも意識しようと思います)。
参考URL
http://javazuki.com/articles/bigdecimal-usage.html#_roundingmode
https://qiita.com/TKR/items/52635175654b9b818b89
2019/11/12 自分用メモ
大小比較(compareTo())が毎回わからなくなる件
BigDecimal three = BigDecimal.valueOf(3);
BigDecimal five = BigDecimal.valueOf(5);
int hoge = three.compareTo(five); // -1
int piyo = three.compareTo(three); // 0
int fuga = five.compareTo(three); // 1
Author And Source
この問題について(Javaの金額計算で学んだこと), 我々は、より多くの情報をここで見つけました https://qiita.com/nobi_tum/items/e2db023c55f04d3edeb4著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .