FLP30-C. 浮動小数点変数をループカウンタに使用しない
浮動小数点数は実数値を表現しているため、単純な有理数などは正確に表現できると考えられがちであるが、これは間違いである。浮動小数点数には整数と同様に精度の限界があり、全ての実数を厳密に表現できるわけではない。10進数としては小さい桁数で表現できる値が、2進数の浮動小数点数としては正確に表現できない場合がある。
加えて、浮動小数点数は大きな値を表現できるため、そのような大きな値の有効桁数全てを表現できると考えられることが多いが、これもまた間違いである。浮動小数点数は固定長の精度ビット(significand とも呼ばれる)と指数ビット(exponent)を使って広い範囲の値を表現するため、有効桁数はごく限られたものになる。
処理系によって精度の限界は異なる。コードの可搬性を維持するためには、ループカウンタに浮動小数点変数を使ってはいけない。この話題については、[Goldberg 1991] を参照すること。
違反コード
以下の違反コード例では、ループカウンタとして浮動小数点変数を使っている。10進小数 0.1
は2進数では循環小数になるので、2進の浮動小数点数として正確に表現することができない。処理系によって異なるが、ループは 9 回あるいは 10 回繰り返される。
void func(void) {
for (float x = 0.1f; x <= 1.0f; x += 0.1f) {
/* ループは9回あるいは10回繰り返す */
}
}
たとえば、GCC あるいは Microsoft Visual Studio 2013 でコンパイルし、x86 プロセッサで実行した場合、ループは 9 回しか評価されない。
適合コード
以下の適合コードでは、ループカウンタに整数を使い、ループカウンタから浮動小数点の数値を計算している。
#include <stddef.h>
void func(void) {
for (size_t count = 1; count <= 10; ++count) {
float x = count / 10.0f;
/* ループはちょうど10回繰り返す */
}
}
違反コード
次の違反コード例では、浮動小数点数であるループカウンタをインクリメントしているが、この精度では値が小さすぎてカウンタの値が変わらない。
void func(void) {
for (float x = 100000001.0f; x <= 100000010.0f; x += 1.0f) {
/* ループが終了しない可能性がある */
}
}
このコードは多くの処理系で無限ループになる。
適合コード
次の適合コードでは、ループカウンタに整数を使い、カウンタの値を使って浮動小数点数の値を計算している。変数 x
は、先の違反コード例のような丸めエラーが発生しないように計算を行なっている。
void func(void) {
for (size_t count = 1; count <= 10; ++count) {
float x = 1.00000000.0f + (count * 1.0f);
/* ループはちょうど10回繰り返す */
}
}
リスク評価
ループカウンタに浮動小数点変数を使用するとプログラムの予期せぬ動作につながる可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
FLP30-C |
低 |
中 |
低 |
P6 |
L2 |
自動検出
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Clang | 3.9 (prerelease) | cert-flp30-c |
Checked by clang-tidy |
CodeSonar | 4.2 | LANG.STRUCT.LOOP.FPC | Float-typed loop counter |
|
|
|
|
ECLAIR | 1.2 | CC2.FLP30 | Fully implemented |
5.0 |
|
Can detect violations of this rule with CERT C Rule Pack |
|
9.5.6 |
39 S |
Fully implemented |
|
Parasoft C/C++test | 9.5 | MISRA-065 | Fully implemented |
Polyspace Bug Finder | R2016a | MISRA2012-RULE-14_1, MISRA2012-DIR-1_1 | Fully implemented |
PRQA QA-C | 8.2 | 3340 | Partially implemented |
SonarQube C/C++ Plugin | 3.11 | S2193 | Fully implemented |
関連するガイドライン
SEI CERT C++ Coding Standard | FLP30-CPP. Do not use floating-point variables as loop counters |
Java セキュアコーディングスタンダード CERT/Oracle 版 | NUM09-J. 浮動小数点数型変数をループカウンタとして使用しない |
ISO/IEC TR 24772:2013 | Floating-Point Arithmetic [PLF] |
MISRA C:2012 | Directive 1.1 (required) Rule 14.1 (required) |
参考資料
[Goldberg 1991] | |
[Lockheed Martin 05] | AV Rule 197 |
翻訳元
これは以下のページを翻訳したものです。
FLP30-C. Do not use floating-point variables as loop counters (revision 96)