FLP36-C. 整数型から浮動小数点型への変換時に精度を確保する
幅の狭い整数型からより広い整数型への変換は、数値の大きさに影響を及ぼすことなく行うことができる。一方で、整数値は値を正確に表現するが、浮動小数点数には精度の限界がある。C 標準、6.3.1.4 節、第2パラグラフには次のように記されている [ISO/IEC 9899:2011]。
整数型の値を実浮動小数点型に型変換する場合、変換する値が新しい型で正確に表現できるとき、その値は変わらない。変換する値が、表現しうる値の範囲内にあるが正確に表現できない場合、その結果は、その値より大きく最も近い表現可能な値、またはその値より小さく最も近い表現可能な値のいずれかを処理系定義の方法で選ぶ。変換する値が表現しうる値の範囲外にある場合、その動作は未定義とする。一部の暗黙の型変換の結果を表現するには、新しい型の範囲と精度よりも大きいものが必要となる場合がある (6.3.1.8 および 6.8.6.4 を参照)。
整数型から浮動小数点型への十分な精度が確保できない変換は、精度の消失(最下位有効ビットの消失)につながる可能性がある。精度の消失が発生しても実行時の例外は発生しない。
違反コード
次の違反コード例では、long int
型の大きい値が float
型に変換されているが、値が表現できるかどうかを確認していない。
#include <stdio.h>
int main(void) {
long int big = 1234567890;
float approx = big;
printf("%d\n", (big - (long int)approx));
return 0;
}
たいていの浮動小数点ハードウェアにおいて、float
型で表現することのできる 1234567890
に最も近い値は 1234567844
である。したがって、このプログラムは -46
を出力する。
適合コード
次の適合コードでは、float
を double
に置き換えている。さらに、静的アサートを使い、double
型で精度を消失せずに任意の long int
値を表現できることを確認している (「INT35-C. 整数型の精度を正しく求める」および「MSC11-C. 診断テストはアサートを使って組み込む」を参照)。
#include <assert.h>
#include <stdio.h>
#include <float.h>
#include <limits.h>
extern size_t popcount(uintmax_t); /* See INT35-C */
#define PRECISION(umax_value) popcount(umax_value)
int main(void) {
assert(PRECISION(LONG_MAX) <= DBL_MANT_DIG * log2(DBL_MANT_DIG));
long int big = 1234567890;
double approx = big;
printf("%d\n", (big - (long int)approx));
return 0;
}
違反コードと異なり、このコードでは 0
が出力される。つまり、値が変わることなく整数値 1234567890
が double
型で表現可能であることを意味している。
リスク評価
整数型から浮動小数点型への変換で十分な精度を確保できない場合、精度の消失 (最下位有効ビットの消失) につながる可能性がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
FLP36-C | 低 | 低 | 中 | P2 | L3 |
関連するガイドライン
CERT C コーディングスタンダード | DCL03-C. 定数式の値をテストするには静的アサートを使う |
Java セキュアコーディングスタンダード CERT/Oracle 版 | NUM13-J. プリミティブ整数を浮動小数点数に変換する際、精度を低下させない |
参考資料
[ISO/IEC 9899:2011] | 6.3.1.4 「実浮動小数点型及び整数型」 |
翻訳元
これは以下のページを翻訳したものです。
FLP36-C. Preserve precision when converting integral values to floating-point type (revision 52)