INT35-C. 整数型の精度を正しく求める
C の整数型で注意すべき特性として、サイズ と 精度 がある。サイズはオブジェクト表現に使われるバイト数のことで、一般に型やその型のオブジェクトの表現に使われるバイト数は sizeof
オペレータで求めることができる。整数型の精度とは値を表現するために使われるビット数のことで、符号やパディングビットを除いたものである。
パディングビットは整数のサイズには影響するが、整数の精度には影響しない。そのため、整数型の精度をサイズから求めようとすると、実際よりも広い範囲の値を表現できると判断してしまう可能性がある。コード中で整数型の精度が必要な場合には、正しい手段で求めること。とくに、実行環境における整数型にパディングビットが含まれている場合や可搬性を重視するプログラムでは、sizeof
オペレータを使って整数型の精度を求めてはいけない。
違反コード
次の違反コード例は、2のべき乗を求める関数である。「INT34-C. 負のビット数のシフトやオペランドのビット数以上のシフトを行わない」に適合し未定義の動作を防ぐため、シフト操作を行う前に、引数 exp
が unsigned int
型の値を表現するビット数よりも小さいことをチェックしている。
#include <limits.h>
unsigned int pow2(unsigned int exp) {
if (exp >= sizeof(unsigned int) * CHAR_BIT) {
/* エラー処理 */
}
return 1 << exp;
}
しかし、このコードを unsigned int
にパディングビットが含まれるような環境で実行すると、チェックは正しく行われない場合がある。例えば、unsigned int
型のサイズが64ビットで精度は48ビットしかない場合、56ビット左シフトを行ってしまう可能性がある。
適合コード
次の適合コードでは、符号無し整数のビット1の数を求める関数 popcount()
を定義している。popcount()
関数を使うことで符号無し、符号付きどちらの整数型の精度も求めることができる。
#include <stddef.h>
#include <stdint.h>
/* Returns the number of set bits */
size_t popcount(uintmax_t num) {
size_t precision = 0;
while (num != 0) {
if (num % 2 == 1) {
precision++;
}
num >>= 1;
}
return precision;
}
#define PRECISION(umax_value) popcount(umax_value)
処理系によっては、PRECISION()
マクロを、指定された型の精度を表す整数定数を返す型総称マクロで置き換えてもよいだろう。このマクロの返り値は、静的アサートの式のなかなど、整数定数が許されるあらゆる場所に使うことができる(「DCL03-C. 定数式の値をテストするには静的アサートを使う」を参照)。例えば IA-32 アーキテクチャにおいて、次に示すような型総称マクロを使うことができる。
#define PRECISION(value) _Generic(value, \
unsigned char : 8, \
unsigned short: 16, \
unsigned int : 32, \
unsigned long : 32, \
unsigned long long : 64, \
signed char : 7, \
signed short : 15, \
signed int : 31, \
signed long : 31, \
signed long long : 63)
PRECISION()
マクロを使った pow2()
関数の改定版を次に示す。
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
extern size_t popcount(uintmax_t);
#define PRECISION(umax_value) popcount(umax_value)
unsigned int pow2(unsigned int exp) {
if (exp >= PRECISION(UINT_MAX)) {
/* エラー処理 */
}
return 1 << exp;
}
処理系固有の詳細
Cray XT CNL 計算ノードでサポートされる CLE (Cray Linux Environment) などのように、popcount()
関数と置き換えて使うことができる _popcnt
インストラクションが提供されているプラットフォームも存在する。
#define PRECISION(umax_value) _popcnt(umax_value)
リスク評価
整数の精度を正しく求めなければ、ビット単位のシフト操作などから未定義の動作が発生する可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
INT35-C |
低 |
低 |
中 |
P2 |
L3 |
参考資料
[Dowd 2006] | Chapter 6, "C Language Issues" |
[C99 Rationale 2003] | 6.5.7, "Bitwise Shift Operators" |
翻訳元
これは以下のページを翻訳したものです。
INT35-C. Use correct integer precisions (revision 27)