NUM04-J. 正確な計算が必要なときは浮動小数点数を使わない
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
適合コード
以下の適合コードでは整数型 (たとえばintなど) を使い、データをドルではなくセント単位で表現している。
int dollar = 100;
int dime = 10;
int number = 7;
System.out.println(
"A dollar less " + number + " dimes is $0." + (dollar - number * dime)
);
このコードでは以下のような正しい出力が得られる。
A dollar less 7 dimes is $0.30
適合コード
以下の適合コードでは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 |
自動検出
浮動小数点数演算の自動検出は容易である。しかし、精度が不十分な計算を行うコードを検出するのは一般に不可能である。正確に表現することのできない浮動小数点数リテラルを警告する、といったヒューリスティックな検査は役に立つかもしれない。
| ツール | バージョン | チェッカー | 説明 |
|---|---|---|---|
| Parasoft Jtest | 2024.2 | CERT.NUM04.UBD | Do not use "float" and "double" if exact answers are required |
関連ガイドライン
|
VOID FLP02-CPP. Avoid using floating point numbers when precise computation is needed |
|
| ISO/IEC TR 24772:2010 |
Floating-Point Arithmetic [PLF] |
参考文献
|
Item 48, "Avoid |
|
|
Puzzle 2, "Time for a Change" |
|
|
|
|
|
[IEEE 754] |
|
|
[JLS 2015] |
|
| [Seacord 2015] |
|
翻訳元
これは以下のページを翻訳したものです。
NUM04-J. Do not use floating-point numbers if precise computation is required (revision 107)
