オペランドのsigned/unsigned判定マクロ

算術変換の検証をする上で、対象の整数値がsignedかunsignedかを判定するマクロを作成してみました。

#define <limits.h>
#define IS_SIGNED(x) ( \
(( (x)>>1)&(1L<<(sizeof(x)*CHAR_BIT-1)))!= \
((~(x)>>1)&(1L<<(sizeof(x)*CHAR_BIT-1))) )

というものです。

このマクロのからくりですが…

  1. 対象の整数について、その最上位ビットが「0」の場合と「1」場合の、それぞれの内部表現(ビット列)を作成します。
  2. 最上位ビットを反転させた値を作るのに、単純にビット反転しています(最上位ビット以外のビットには用が無いため)。
  3. 最上位ビットが「0」の場合と「1」の場合、それぞれの場合において、1ビット左シフトした時に空きビットにセットされた値を比較し、相違があれば算術シフトが発生→signed型、一致すれば論理シフトが発生→unsigned型…という判定を行っています。
  4. 上の理由から、「負の整数値の右ビットシフトにおいて算術シフトが発生する」処理系にのみ通用するマクロとなっています。
  5. また、char型、unsigned char型の値には対応していません(値の評価時にint型へ拡張されるため)。

2^n-1のマスク値を作成するのに、(1L<<(sizeof(x)*CHAR_BIT-1))として定数リテラルを「1L」と記述しているのは、「1」だとint型として扱われるためです(int型として扱われると不都合なのは、64ビット左シフト時に、gccから「左シフトしすぎ!」と警告を出されるためです)。

■2014/06/26 追記
こんな大掛かりなことしなくても、

#define IS_SIGNED(x) ((x)<0 || ~(x)<0)

これでいけました(汗)。