QA@IT

C++で同じ値が表記によって型が変わることがある?

5446 PV

Mac OS X 10.7でg++(4.2.1と4.7.2)やclang++ 4.2を使って64bitバイナリを作っています。

このような環境において、

  • 整数リテラル -0x80000000
  • 整数リテラル -2147483648
  • std::numeric_limits<int>::min() の戻り値

以上3つは同じ値を表しているものと理解していますが、
この3つがそれぞれ型が異なるような結果が得られてしまいます。
これはこういうものなのでしょうか?

試しに以下のようなソースを書きました:

#include <iostream>
#include <limits>
#include <typeinfo>
#define PRINT_TYPE(x) std::cout << #x << ": " << typeid(x).name() << std::endl
#define PRINT_VALUE(x) std::cout << #x << ": " << typeid(x).name() << "(" << x << ")" <<std::endl
int main()
{
  PRINT_TYPE(int);
  PRINT_TYPE(long int);
  PRINT_TYPE(long long int);
  PRINT_TYPE(unsigned int);
  PRINT_TYPE(unsigned long int);
  PRINT_TYPE(unsigned long long int);
  PRINT_VALUE(std::numeric_limits<int>::min());
  PRINT_VALUE(std::numeric_limits<int>::max());
  PRINT_VALUE(-2147483648);
  PRINT_VALUE(-0x80000000);
  PRINT_VALUE(-0x80000000l);
  int v = -2147483648;
  PRINT_VALUE(v);
  v = -0x80000000;
  PRINT_VALUE(v);
  return -0x80000000 == -2147483648 ? 0 : 1;
}

私の環境(Mac OS X 10.7、g++ 4.2.1 or g++ 4.7.2 or clang++ 4.2.1)でのコンパイル結果は次のとおりです:

int: i
long int: l
long long int: x
unsigned int: j
unsigned long int: m
unsigned long long int: y
std::numeric_limits<int>::min(): i(-2147483648)
std::numeric_limits<int>::max(): i(2147483647)
-2147483648: l(-2147483648)
-0x80000000: j(2147483648)
-0x80000000l: l(-2147483648)
v: i(-2147483648)
v: i(-2147483648)
[1]    35925 exit 1     ./a

私は-2147483648-0x80000000もi(int)とみなされると理解していましたが、
それぞれl(long int)、j(unsigned int)とみなされています。
そのため-2147483648-0x80000000を比較しても一致しません(違う値と見なされているため)。

念の為ideoneでも実行してみました:
http://ideone.com/ErUnhL

int: i
long int: l
long long int: x
unsigned int: j
unsigned long int: m
unsigned long long int: y
std::numeric_limits<int>::min(): i(-2147483648)
std::numeric_limits<int>::max(): i(2147483647)
-2147483648: m(2147483648)
-0x80000000: j(2147483648)
-0x80000000l: m(2147483648)
v: i(-2147483648)
v: i(-2147483648)

こちらはリテラルがそれぞれm(unsigned long int)、j(unsigned int)のように両方共unsignedと見なされています。

回答

私は-2147483648も-0x80000000もi(int)とみなされると理解していましたが、
それぞれl(long int)、j(unsigned int)とみなされています。
そのため-2147483648と-0x80000000を比較しても一致しません(違う値と見なされているため)。

正確な仕様とかは詳しくないので判りませんが、次の通りになっているのだと思われます。

  • -21474836482147483648 が long で単項演算子 -0xFFFFFFFF80000000-2147483648 (long)
  • -0x800000000x80000000 が unsigned int で単項演算子 -0x800000002147483648 (unsigned int)

念の為ideoneでも実行してみました:
http://ideone.com/ErUnhL
こちらはリテラルがそれぞれm(unsigned long int)、j(unsigned int)のように両方共unsignedと見なされています。

32ビット環境のようなので次の通り・・・

  • -21474836482147483648 が unsigned int で単項演算子 -0x800000002147483648 (unsigned int)
  • -0x800000000x80000000 が unsigned int で単項演算子 -0x800000002147483648 (unsigned int)

と思ったのですが -2147483648unsigned long になっていますね・・・

なぜかは判りませんが 2147483648unsigned long になるようです。

#include <iostream>
#include <limits>
#include <typeinfo>
#define PRINT_TYPE(x) std::cout << #x << ": " << typeid(x).name() << std::endl
#define PRINT_VALUE(x) std::cout << #x << ": " << typeid(x).name() << "(" << x << ")" <<std::endl
int main()
{
    PRINT_TYPE(int);
    PRINT_TYPE(long int);
    PRINT_TYPE(long long int);
    PRINT_TYPE(unsigned int);
    PRINT_TYPE(unsigned long int);
    PRINT_TYPE(unsigned long long int);
    PRINT_VALUE(2147483647);
    PRINT_VALUE(2147483648);
    return 0;
}
int: i
long int: l
long long int: x
unsigned int: j
unsigned long int: m
unsigned long long int: y
2147483647: i(2147483647)
2147483648: m(2147483648)

c++11 だとまた挙動が変わるようです。

int: i
long int: l
long long int: x
unsigned int: j
unsigned long int: m
unsigned long long int: y
2147483647: i(2147483647)
2147483648: x(2147483648)

ちなみに VC で試すと次の通りになり

#include <iostream>
#include <limits>
#include <typeinfo>
#define PRINT_TYPE(x) std::cout << #x << ": " << typeid(x).name() << std::endl
#define PRINT_VALUE(x) std::cout << #x << ": " << typeid(x).name() << "(" << x << ")" <<std::endl
int main()
{
    PRINT_TYPE(int);
    PRINT_TYPE(long int);
    PRINT_TYPE(long long int);
    PRINT_TYPE(unsigned int);
    PRINT_TYPE(unsigned long int);
    PRINT_TYPE(unsigned long long int);
    PRINT_VALUE(2147483647);
    PRINT_VALUE(2147483648);
    PRINT_VALUE(-2147483648);
    PRINT_VALUE(-0x80000000);
    PRINT_VALUE(-0x80000000l);
    return 0;
}
int: int
long int: long
long long int: __int64
unsigned int: unsigned int
unsigned long int: unsigned long
unsigned long long int: unsigned __int64
2147483647: int(2147483647)
2147483648: unsigned long(2147483648)
-2147483648: unsigned long(2147483648)
-0x80000000: unsigned int(2147483648)
-0x80000000l: unsigned long(2147483648)

この時の警告メッセージで検索するとそれらしいことが書かれています。

コンパイラの警告 (レベル 2) C4146

2147483648 の型は int ではなく unsigned int

と書かれているのが疑問ですが。

編集 履歴 (2)
  • 「-は単項演算子である」というご指摘でかなり納得出来ました。先に数字部分が解釈されて、後に-で負数に変わるのであれば納得の結果です。
    また、VCのドキュメントのご紹介もありがとうございました。そのまんまのことが書かれてましたね。
    -
  • 単に2147483648は32bit環境のintで表せないからでは?
    参考:https://msdn.microsoft.com/ja-jp/library/7fh3a000.aspx
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。