Java 言語には2種類のプリミティブ浮動小数点数型 float と double が存在する。これらの型はそれぞれ、32ビット単精度浮動小数点数と64ビット倍精度浮動小数点数であり、その動作は IEEE 754 で規定されている[IEEE 754]。浮動小数点数型では仮数部のビット長は固定である。そのため、無理数(たとえば円周率π)を正確に表現することはできない。さらに、仮数部は2進数表現であるため、10進数 0.1 などのような多くの10進有理数も正確には表現できない。なぜならば、このような数は2進数表現では無限長となるからだ。
通貨の換算のように正確な計算が必要な場合、浮動小数点数型を使ってはならない。代わりに、必要な値を正確に表現できる表現形式を使うこと。
正確な計算が不要であれば浮動小数点数表現を使ってもよい。そのような場合には、累積する計算誤差の最大値を慎重かつ体系的に見積り、許容範囲内に収まることを確認しなくてはならない。問題を正しく理解するためには、数値解析を行うことを検討するとよいだろう。このトピックに関する入門書として Goldberg の著作を参照[Goldberg 1991]。
違反コード
以下の違反コード例では基本的な通貨の計算を行っている。
double dollar = 1.00; double dime = 0.10; int number = 7; System.out.println("A dollar less " + number + " dimes is $" + (dollar - number * dime) );
値 0.10 は Java の浮動小数点数では正確に表現できないため、多くのプラットフォームでは以下のような結果が出力される(仮数部を2進表現するどのような浮動小数点数においても同様の結果となる)。
A dollar less 7 dimes is $0.29999999999999993
適合コード
以下の適合コードでは整数型(たとえばlongなど)を使い、データをドルではなくセント単位で表現している。
long dollar = 100; long dime = 10; int number = 7; System.out.println ("A dollar less " + number + " dimes is " + (dollar - number * dime) + " cents" );
このコードでは以下のような正しい出力が得られる。
A dollar less 7 dimes is 30 cents
適合コード
以下の適合コードでは10進数を正確に表現できる BigDecimal 型を用いている。ほとんどのプラットフォームでは、BigDecimal を使った計算はプリミティブ型を使った計算よりも効率が悪い。効率の悪化がもたらす意味はアプリケーションによって異なる。
import java.math.BigDecimal; BigDecimal dollar = new BigDecimal("1.0"); BigDecimal dime = new BigDecimal("0.1"); int number = 7; System.out.println ("A dollar less " + number + " dimes is $" + (dollar.subtract(new BigDecimal(number).multiply(dime) )) );
このコードは以下を出力する。
A dollar less 7 dimes is $0.3
リスク評価
正確な計算が必要な場合に浮動小数点数表現を用いると、精度が失われたり、計算結果が間違った値になる可能性がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
NUM04-J | 低 | 中 | 高 | P2 | L3 |
自動検出
浮動小数点数演算の自動検出は容易である。精度が不十分な計算を行うコードを検出するのは一般に不可能である。正確に表現することのできない浮動小数点リテラルを警告する、といったヒューリスティックな検査は役に立つかもしれない。
関連ガイドライン
The CERT C Secure Coding Standard | FLP02-C. Avoid using floating point numbers when precise computation is needed |
The CERT C++ Secure Coding Standard | FLP02-CPP. Avoid using floating point numbers when precise computation is needed |
ISO/IEC TR 24772:2010 | Floating-Point Arithmetic [PLF] |
参考文献
[Bloch 2008] | Item 48. Avoid float and double if exact answers are required |
[Bloch 2005] | Puzzle 2. Time for a Change |
[Goldberg 1991] | |
[IEEE 754] | |
[JLS 2005] | §4.2.3, "Floating-Point Types, Formats, and Values" |
翻訳元
これは以下のページを翻訳したものです。
NUM04-J. Do not use floating-point numbers if precise computation is required (revision 67)