腹立ったので記事化。
BigDecimalという、不動小数点を扱うときに精密に計算する際に利用することを推奨されるクラスがあります。
んで、これのインスタンス化のときにDouble使うとズレることがある、という話。
背景
一種の会計ソフトのレシート出力を作っていて、
111.11
円を8つ、つまり 888.88
を出力しようとすると
なぜか 888.87...
になってしまって怒り狂っていた
原因
BigDecimalのインスタンス化のときにdoubleを利用すると、
その時点で正確な値にはならない場合があるようです
追記:
エライ人たちにご指摘いただき、潜ってみたら書いてた ^q^
0.1
とかをdoubleで渡すと、正確な不動小数点表現(64bit表現)に変換されてしまい 0.100...551..
とかになるらしい
/**
* Constructs a new {@code BigDecimal} instance from the 64bit double
* {@code val}. The constructed big decimal is equivalent to the given
* double. For example, {@code new BigDecimal(0.1)} is equal to {@code
* 0.1000000000000000055511151231257827021181583404541015625}. This happens
* as {@code 0.1} cannot be represented exactly in binary.
* <p>
* To generate a big decimal instance which is equivalent to {@code 0.1} use
* the {@code BigDecimal(String)} constructor.
*
* @param val
* double value to be converted to a {@code BigDecimal} instance.
* @throws NumberFormatException
* if {@code val} is infinity or not a number.
*/
public BigDecimal(double val) {
例
import java.math.BigDecimal
fun main(args: Array<String>) {
val seed = 888.88
val fromDouble = BigDecimal(seed)
val fromValueOf = BigDecimal.valueOf(seed)
println("from double -> $fromDouble")
println("from valueOf -> $fromValueOf")
}
from double -> 888.8799999999999954525264911353588104248046875
from valueOf -> 888.88
解決策
Stringにしてインスタンス化のときに渡してやると、ズレずにできました。よかったよかった。
BigDecimal.valueOf(double)
とすると正確にdoubleから変換できるようです。
(@blhsrwznrghfzpr さん、ご指摘ありがとうございました!)
みんなちゃんとドキュメント読もう ( ^ω^)