【C++】組み込み型の使用バイト数と最小/最大値の調べ方

# はじめに
C++では組み込み型は基本型(fundamental type)とも呼ばれます。実はこの組み込み型、同じintなのにCPUのビット数やコンパイラによって確保されるバイト数が異なり最大値最小値も変わってしまうということが起こります。

この記事では、使用バイト数と最小/最大値を調べる方法を紹介します。

# 調査方法
次のコードを書きました。
手元で走らせるだけでバイト数と最小/最大値がわかります。

```cpp:dump_bound.cpp
#include <iostream>
#include <limits>

#define dump(var) cout << string(#var) << ": " << var << endl;

using namespace std;

int main(void) {
  dump(sizeof(char));
  dump((int)numeric_limits<char>::min());
  dump((int)numeric_limits<char>::max());
  dump(sizeof(unsigned char));
  dump((int)numeric_limits<unsigned char>::min());
  dump((int)numeric_limits<unsigned char>::max());
  dump(sizeof(short));
  dump(numeric_limits<short>::min());
  dump(numeric_limits<short>::max());
  dump(sizeof(unsigned short));
  dump(numeric_limits<unsigned short>::min());
  dump(numeric_limits<unsigned short>::max());
  dump(sizeof(int));
  dump(numeric_limits<int>::min());
  dump(numeric_limits<int>::max());
  dump(sizeof(unsigned int));
  dump(numeric_limits<unsigned int>::min());
  dump(numeric_limits<unsigned int>::max());
  dump(sizeof(long));
  dump(numeric_limits<long>::min());
  dump(numeric_limits<long>::max());
  dump(sizeof(unsigned long));
  dump(numeric_limits<unsigned long>::min());
  dump(numeric_limits<unsigned long>::max());
  dump(sizeof(long long));
  dump(numeric_limits<long long>::min());
  dump(numeric_limits<long long>::max());
  dump(sizeof(unsigned long long));
  dump(numeric_limits<unsigned long long>::min());
  dump(numeric_limits<unsigned long long>::max());
  dump(sizeof(float));
  dump(numeric_limits<float>::min());
  dump(numeric_limits<float>::max());
  dump(sizeof(double));
  dump(numeric_limits<double>::min());
  dump(numeric_limits<double>::max());
  dump(sizeof(long double));
  dump(numeric_limits<long double>::min());
  dump(numeric_limits<long double>::max());
  return 0;
}
```


以下の環境で実行しました。

```zsh
% g++-11 --version
g++-11 (Homebrew GCC 11.1.0_1) 11.1.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```

# 結果

環境に依存するので参考程度に！!
浮動小数点の最小値は"最小の正の値"を表しています。

|基本型 |使用バイト数 |最小値 |最大値 | 
|---|--:|--:|--:|
|char | 1 | -128 | 127 | 
|unsigned char | 1 | 0 | 255 | 
|short | 2 | -32768 | 32767 | 
|unsigned short | 2 | 0 | 65535 | 
|int | 4 | -2147483648 | 2147483647 | 
|unsigned int | 4 | 0 | 4294967295 | 
|long | 8 | -9223372036854775808 | 9223372036854775807 | 
|unsigned long | 8 | 0 | 18446744073709551615 | 
|long long | 8 | -9223372036854775808 | 9223372036854775807 | 
|unsigned long long | 8 | 0 | 18446744073709551615 | 
|float | 4 | 1.17549e-38 | 3.40282e+38 | 
|double | 8 | 2.22507e-308 | 1.79769e+308 | 
|long double | 16 | 3.3621e-4932 | 1.18973e+4932 | 

<details>
<summary>生出力</summary><div>

```
% ./a.out    
sizeof(char): 1
(int)numeric_limits<char>::min(): -128
(int)numeric_limits<char>::max(): 127
sizeof(unsigned char): 1
(int)numeric_limits<unsigned char>::min(): 0
(int)numeric_limits<unsigned char>::max(): 255
sizeof(short): 2
numeric_limits<short>::min(): -32768
numeric_limits<short>::max(): 32767
sizeof(unsigned short): 2
numeric_limits<unsigned short>::min(): 0
numeric_limits<unsigned short>::max(): 65535
sizeof(int): 4
numeric_limits<int>::min(): -2147483648
numeric_limits<int>::max(): 2147483647
sizeof(unsigned int): 4
numeric_limits<unsigned int>::min(): 0
numeric_limits<unsigned int>::max(): 4294967295
sizeof(long): 8
numeric_limits<long>::min(): -9223372036854775808
numeric_limits<long>::max(): 9223372036854775807
sizeof(unsigned long): 8
numeric_limits<unsigned long>::min(): 0
numeric_limits<unsigned long>::max(): 18446744073709551615
sizeof(long long): 8
numeric_limits<long long>::min(): -9223372036854775808
numeric_limits<long long>::max(): 9223372036854775807
sizeof(unsigned long long): 8
numeric_limits<unsigned long long>::min(): 0
numeric_limits<unsigned long long>::max(): 18446744073709551615
sizeof(float): 4
numeric_limits<float>::min(): 1.17549e-38
numeric_limits<float>::max(): 3.40282e+38
sizeof(double): 8
numeric_limits<double>::min(): 2.22507e-308
numeric_limits<double>::max(): 1.79769e+308
sizeof(long double): 16
numeric_limits<long double>::min(): 3.3621e-4932
numeric_limits<long double>::max(): 1.18973e+4932
```
</div></details>

こちらの表とも一致してそうです。
https://atcoder.jp/contests/apg4b/tasks/APG4b_y?lang=ja#:~:text=%E4%B8%BB%E3%81%AA%E7%AC%A6%E5%8F%B7%E4%BB%98%E3%81%8D%E6%95%B4%E6%95%B0%E5%9E%8B

# 余談： intやlongを使うよりuint64_tで統一したほうが楽そう?
AtCoderでは`int??_t`系が推奨されています。これらの型はC++11から導入され、固定長です。
> int??_t系やuint??_t系以外については、環境によっては上の表と異なることがあります。
(略)
> たくさんあってどう使い分けるのか分からなくなるかもしれませんが、 int??_t系やuint??_t系を使うことをおすすめします。 極端な話ですが、使用するメモリ量をそこまで気にしないのであれば全てint64_t型かuint64_t型を使ってしまうという手もあります。
https://atcoder.jp/contests/apg4b/tasks/APG4b_y?lang=ja

プロダクトでも整数型を64bitに統一できると、キャストについて事故を起こすリスクや開発コストを減らせそうでいいですね。

ただし、int64_t に罠があるという意見もあるそうです。

- > 処理系によっては1バイトが8ビットでないことがあり、そういった環境では8ビットの乗数幅を持つ整数型が定義されていない可能性がある。
  - https://cpprefjp.github.io/reference/cstdint/int64_t.html
-  accumulate()の第三引数に暗黙的にintを渡してしまいオバーフロー
  - https://airscarlet.com/atcoder-accumulate_64bit/

# 参考
intの最大値と最小値の調べ方
https://stackoverflow.com/questions/1855459/maximum-value-of-int
数値型
https://atcoder.jp/contests/apg4b/tasks/APG4b_y?lang=ja
Fixed width integer types
https://en.cppreference.com/w/cpp/types/integer