アカウンティング・サース・ジャパンの開発でトラブった箇所をメモっていこうとおもう。
以前開発を海外の外注に出していて、安易にfloatやdoubleを使用して計算している箇所があり、自分でゴリっと修正した時のお話です。
#金額の小数計算にfloatとdoubleは使用禁止
基本的にfloatやdoubleなどの2進浮動小数点数の型は金額計算で使ってはいけない。どうしてか?それは小数点計算において正確に
計算できないからだ。
どういうことかというと
float a = 1.03;
float b = 0.42;
System.out.println(a-b)
0.6100000000000001
となります。正確な金額出せない・・・。
#金額の小数計算にBigDecimalを使用
正確に金額を計算したい場合はBigDecimalを使用します。
import java.math.BigDecimal
BigDecimal a = new BigDecimal("1.03");
BigDecimal b = new BigDecimal("0.42");
BigDecimal c = a.subtract(b);
System.out.println(c.toString());
0.61
BigDecimalは計算符号で記述できないのでとにかく見にくいし、めんどいと思われてしまうらしい。
#BigDecimalの罠
ここでBigDecimalを使うと大丈夫かというと罠がある。
BigDecimalでの初期代入を間違えるとせっかくBigDecimalで正確に計算しようとしているのに意味がなくなってしまう。
どういうことかというと小数を代入する時に数値で代入してはいけないということである。文字で代入しないといけない。
BigDecimal rate1 = new BigDecimal("0.1");
BigDecimal rate2 = new BigDecimal(0.1);
これのちがいである
前者は文字列の引数で渡されるので正確に計算できる。
後者は0.1はdouble引数のコンストラクタに流れるので一旦2進浮動小数点扱いとしてから代入してしまう罠だ気をつけろ!
double引数のコンストラクタ自体いらない、コンパイルエラーにして欲しいと思ってしまう。
#最後に
外注がいたる所でfloatやdoubleを使用していて直すのが面倒だったのか小手先対応で誤差を是正しようと怖い端数処理をしていた。びびった。
完全な正確性を求めない画面構成の割合計算や座標計算ではプログラムしやすい計算も早いので手軽に使用してしまうdoubleやfloatではあるが金額を扱うシステムを構築する人にとっては”金額にdoubleやfloatは使用禁止”を初めから知っておきたい常識である。あとで1円で泣くことになるからね。
そんなオフショアもやめ完全内製化とした弊社は現在システムを一緒に作ってくれる仲間を募集中です。