JPCERT コーディネーションセンター

NUM04-J. 正確な計算が必要なときは浮動小数点数を使わない

Java 言語には2種類のプリミティブ浮動小数点数型 floatdouble が存在する。これらの型はそれぞれ、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)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter