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

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

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
適合コード

以下の適合コードでは整数型 (たとえば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
関連ガイドライン

SEI CERT C コーディングスタンダード

FLP02-C. 精度の高い計算が必要な場合は浮動小数点数の使用を避ける

SEI CERT C++ Secure Coding Standard

VOID 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 2015]

§4.2.3, Floating-Point Types, Formats, and Values

[Seacord 2015]

Image result for video icon NUM04-J. Do not use floating-point numbers if precise computation is required LiveLesson

翻訳元

これは以下のページを翻訳したものです。

NUM04-J. Do not use floating-point numbers if precise computation is required (revision 107)

Top へ

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