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)



