FLP00-C. 浮動小数点数の限界を理解する
C 言語では、計算に浮動小数点数を使用することができる。C99 は、浮動小数点数に関して規格合致処理系の条件を規定しているが、競合する浮動小数点方式が複数存在するため、処理系に固有の浮動小数点数の表現についてはほとんど保証していない。
処理系に関係なく、浮動小数点数は有限精度の数値であり、丸め処理に関する誤りが生じやすい(「FLP01-C. 浮動小数点式の演算の順序に注意する」、および「FLP02-C. 精度の高い計算が必要な場合は浮動小数点数の使用を避ける」を参照)。
最も一般的な浮動小数点方式は IEEE 754 標準で規定されている。古い浮動小数点方式には IBM 浮動小数点表現(IBM/370 とも呼ばれる)がある。これらの方式は、表現可能な値の精度と範囲が異なる。そのため、同じ値がすべて同じように表現されるわけではなく、バイナリレベルでの互換性がなく、誤差率も異なる。
処理系固有の浮動小数点方式に対する C99 による保証が十分でないため、精度や範囲について想定することはできない。可搬性を意図したコードでない場合でも、選択したコンパイラに関して、あらゆる最適化レベルでの動作を十分に理解しておく必要がある。
以下に、精度の限界の例を簡単に示す。以下のコードは 1/3 の 10 進数表現を、小数点以下 50 桁の 10 進数として出力する。望ましい結果は、数字の 3 が 50 個出力されることである。
#include <stdio.h> int main(void) { float f = 1.0 / 3.0; printf("Float is %.50f\n", f); return 0; }
64 ビット Linux 上で GCC 4.1 を使った場合、このプログラムは次のような出力を生成する。
Float is 0.33333334326744079589843750000000000000000000000000
Windows XP で Microsoft Visual C++ Compiler 9.0 を使った場合、このプログラムは次のような出力を生成する。
Float is 0.33333334326744080000000000000000000000000000000000
さらに、最適化のレベルによって、コンパイラが浮動小数点型変数の処理方法を変えることがある [Gough 2005]。
double a = 3.0; double b = 7.0; double c = a / b; if (c == a / b) { printf("Comparison succeeds\n"); } else { printf("Unexpected result\n"); }
IA-32 Linux 上で GCC 3.4.4 を使って最適化レベル 1 以上でコンパイルした場合、または テスト用の IA-32 Windows XP 上で Microsoft Visual C++ Express 8.0 を使ってコンパイルした場合、このコードは次のような出力を生成する。
Comparison succeeds
IA-32 Linux 上で GCC 3.4.4 を使って最適化処理をオフにした場合、このコードは次のような出力を生成する。
Unexpected result
このように最適化レベルによって動作が異なる理由は、IA-32 マシン上の Linux では計算中の精度を上げるために、x87 FPU の内部拡張精度モードが使われるためである。c への代入結果をメモリに格納する際、FPU は double に合うように結果の丸め処理を自動的に行う。そのため、メモリから再度読み取った値は、精度が拡張されている内部表現とは異なってしまう。Windows は拡張精度モードを使用しないため、すべての計算は double 精度で行われ、メモリと FPU 内部で格納されている値の精度は同一である。gcc の場合は、最適化レベル 1 以上でコンパイルすることで、メモリへの不必要な格納が行われなくなり、すべての計算が FPU 内で拡張精度を使用して行われる [Gough 2005]。
リスク評価
浮動小数点数の限界を理解していなければ、予期せぬ計算結果や例外状態が発生し、場合によってはデータの完全性が損なわれることがある。
レコメンデーション | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
FLP00-C | 中 | 中 | 高 | P4 | L3 |
参考情報
- [Gough 2005] Section 8.6, "Floating-point issues"
- [IEEE 754 2006]
- [ISO/IEC 9899:1999] Section 5.2.4.2.2, "Characteristics of floating types <float.h>"
- [ISO/IEC PDTR 24772] "PLF Floating Point Arithmetic"
翻訳元
これは以下のページを翻訳したものです。
FLP00-C. Understand the limitations of floating point numbers