はじめに
同じ型どうしの計算でも、その型で扱える値域を超えることが分かっている場合は、
大きな型にキャストして演算する必要があります。
皆さんも1度は経験したことがあるのではないでしょうか。
意図した通りの計算結果を得られず、不具合を埋め込んでしまった経験が(笑)
CPUのビット数を超える型を扱う場合は、特に注意が必要です。
例えば、32ビットのシステムで、64ビットの演算を行う場合などです。
本記事では、なぜ意図した結果が得られない場合があるのか、
型の値域を超えるとどうなるのかを説明いたします。
コンパイラの気持ち
C言語では、特に指定のない定数などは INT 型として扱われます。
演算においても、極力 INT 型で済ませようとします。

演算を実行するプロセッサは、表面上は8ビットや16ビットの演算をサポートしている場合もありますが、
内部的にはプロセッサ自身のビット数、例えば32ビットで演算を行ったうえで値を切り詰めています。
そのため、INT 型で演算を行った方が、処理性能が良くなる場合があります。

オーバーフロー
INT 型で表現できる数値は「-2,147,483,648」から「2,147,483,647」です。
演算を行った結果、この値域を超える場合はオーバーフローが発生します。

UNIX 系の OS でオーバーフローといえば、最も有名なのが「2038年問題」です。
実時間を1970年からの経過秒の INT 型で管理しており、2038年でオーバーフローする問題です。
この問題は、「Linux/Windowsにおける2038年問題の対策」で詳しく解説しています。
実際にどのようなことが起きるか見てみましょう。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <stdint.h> #include <stdio.h> int test_case(void) { int a = 10000; int b = 10000; return a * b; } int main(void) { int result32 = test_case(); printf("RESULT: %d\n", result32); return 0; } |
command
$ gcc -m32 -o test32 -O2 main.c
$ ./test32
RESULT: 100000000
演算結果は「100000000」ですので、INT 型で表現できます。
では、次の演算はどうでしょうか。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <stdint.h> #include <stdio.h> int test_case(void) { int a = 10000; int b = 10000; return a * b * 10000; } int main(void) { int result32 = test_case(); printf("RESULT: %d\n", result32); return 0; } |
command
$ gcc -m32 -o test32 -O2 main.c
$ ./test32
RESULT: -727379968
結果の期待値は「1000000000000」ですが、負の値になりました。
INT 型で表現できる地域を超えたため、オーバーフローを起こして正しい結果になりませんね。
間違ったオーバーフローの対応
冒頭でも少し触れましたが、同じ型どうしの計算でも、その型で扱える値域を超える場合は、
大きな型にキャストして演算する必要があります。
先ほどのプログラムを以下のように変更して試してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <stdint.h> #include <stdio.h> long long test_case(void) { int a = 10000; int b = 10000; return a * b * 10000; } int main(void) { long long result64 = test_case(); printf("RESULT: %lld\n", result64); return 0; } |
command
$ gcc -m32 -o test32 -O2 main.c
$ ./test32
RESULT: -727379968
関数の戻り値、呼び出し元で受け取る変数を Long Long 型(64ビット)に変更しましたが、
問題は解決されませんでした。

先ほどの「コンパイラの気持ち」を思い出してみてください。
定数は INT 型として扱われますので、計算部分は「INT型 × INT 型 × INT 型」となり、
INT 型で計算を行った後に、Long Long 型(64ビット)にキャストして戻るコードになります。

正しいオーバーフローの対応
今回のように、32ビットのシステムで64ビットの演算を行う場合など、
プロセッサのビット数を超える型を扱う場合は、特に注意が必要です。
正しくは、以下のように記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <stdint.h> #include <stdio.h> long long test_case(void) { int a = 10000; int b = 10000; return (long long)a * (long long)b * 10000LL; } int main(void) { long long result64 = test_case(); printf("RESULT: %lld\n", result64); return 0; } |
command
$ gcc -m32 -o test32 -O2 main.c
$ ./test32
RESULT: 1000000000000

GCC は、演算式の中のいずれかが Long Long 型であれば大丈夫ですが、
ANSI C で特に規定されていない内容となるため、全ての項を64ビットとしておいた方が無難です。
左辺を優先するコンパイラや、右辺を優先するコンパイラもあります。
ポイント
- 全ての項を Long Long 型(64ビット)にキャストする
- 定数は LL を末尾に付け、Long Long 型として扱うように指定する