このページは、練習問題の解答例や解説のページです。
各種の標準整数型で扱える値の範囲を、std::numeric_limits<> を使って確認してみてください。
標準整数型とは、signed char、short int、int、long int、long long int および、unsigned char、unsigned short int、unsigned int、unsigned long int、unsigned long long int のことです(本編解説)。
#include <limits>
#include <iostream>
int main()
{
std::cout << "signed char: " << static_cast<int>(std::numeric_limits<signed char>::min()) << " ~ " << static_cast<int>(std::numeric_limits<signed char>::max()) << "\n"
<< "unsigned char: " << static_cast<int>(std::numeric_limits<unsigned char>::min()) << " ~ " << static_cast<int>(std::numeric_limits<unsigned char>::max()) << "\n"
<< "short: " << std::numeric_limits<short>::min() << " ~ " << std::numeric_limits<short>::max() << "\n"
<< "unsigned short: " << std::numeric_limits<unsigned short>::min() << " ~ " << std::numeric_limits<unsigned short>::max() << "\n"
<< "int: " << std::numeric_limits<int>::min() << " ~ " << std::numeric_limits<int>::max() << "\n"
<< "unsigned int: " << std::numeric_limits<unsigned int>::min() << " ~ " << std::numeric_limits<unsigned int>::max() << "\n"
<< "long: " << std::numeric_limits<long>::min() << " ~ " << std::numeric_limits<long>::max() << "\n"
<< "unsigned long: " << std::numeric_limits<unsigned long>::min() << " ~ " << std::numeric_limits<unsigned long>::max() << "\n"
<< "long long: " << std::numeric_limits<long long>::min() << " ~ " << std::numeric_limits<long long>::max() << "\n"
<< "unsigned long long: " << std::numeric_limits<unsigned long long>::min() << " ~ " << std::numeric_limits<unsigned long long>::max() << "\n";
}
実行結果:
signed char: -128 ~ 127
unsigned char: 0 ~ 255
short: -32768 ~ 32767
unsigned short: 0 ~ 65535
int: -2147483648 ~ 2147483647
unsigned int: 0 ~ 4294967295
long: -2147483648 ~ 2147483647
unsigned long: 0 ~ 4294967295
long long: -9223372036854775808 ~ 9223372036854775807
unsigned long long: 0 ~ 18446744073709551615
標準整数型で扱える値の範囲は、signed char、unsigned char を除いて、処理系定義なので、実行結果は使っているコンパイラによって異なります。
signed char、unsigned char(char も)の値を出力するとき、普通にやると、文字として出力されます。整数値で出力するには、static_cast で int型にキャストします(本編解説)。
<cstdint> で定義されている型について、sizeof演算子がどのような大きさを返すか調べてみてください。
たとえば次のようにして確認できます。
#include <cstdint>
#include <iostream>
int main()
{
std::cout << "int8_t: " << sizeof(std::int8_t) << "\n"
<< "int16_t: " << sizeof(std::int16_t) << "\n"
<< "int32_t: " << sizeof(std::int32_t) << "\n"
<< "int64_t: " << sizeof(std::int64_t) << "\n"
<< "uint8_t: " << sizeof(std::uint8_t) << "\n"
<< "uint16_t: " << sizeof(std::uint16_t) << "\n"
<< "uint32_t: " << sizeof(std::uint32_t) << "\n"
<< "uint64_t: " << sizeof(std::uint64_t) << "\n"
<< "int_least8_t: " << sizeof(std::int_least8_t) << "\n"
<< "int_least16_t: " << sizeof(std::int_least16_t) << "\n"
<< "int_least32_t: " << sizeof(std::int_least32_t) << "\n"
<< "int_least64_t: " << sizeof(std::int_least64_t) << "\n"
<< "uint_least8_t: " << sizeof(std::uint_least8_t) << "\n"
<< "uint_least16_t: " << sizeof(std::uint_least16_t) << "\n"
<< "uint_least32_t: " << sizeof(std::uint_least32_t) << "\n"
<< "uint_least64_t: " << sizeof(std::uint_least64_t) << "\n"
<< "int_fast8_t: " << sizeof(std::int_fast8_t) << "\n"
<< "int_fast16_t: " << sizeof(std::int_fast16_t) << "\n"
<< "int_fast32_t: " << sizeof(std::int_fast32_t) << "\n"
<< "int_fast64_t: " << sizeof(std::int_fast64_t) << "\n"
<< "uint_fast8_t: " << sizeof(std::uint_fast8_t) << "\n"
<< "uint_fast16_t: " << sizeof(std::uint_fast16_t) << "\n"
<< "uint_fast32_t: " << sizeof(std::uint_fast32_t) << "\n"
<< "uint_fast64_t: " << sizeof(std::uint_fast64_t) << "\n"
<< "intmax_t: " << sizeof(std::intmax_t) << "\n"
<< "uintmax_t: " << sizeof(std::uintmax_t) << "\n";
}
実行結果:
int8_t: 1
int16_t: 2
int32_t: 4
int64_t: 8
uint8_t: 1
uint16_t: 2
uint32_t: 4
uint64_t: 8
int_least8_t: 1
int_least16_t: 2
int_least32_t: 4
int_least64_t: 8
uint_least8_t: 1
uint_least16_t: 2
uint_least32_t: 4
uint_least64_t: 8
int_fast8_t: 1
int_fast16_t: 4
int_fast32_t: 4
int_fast64_t: 8
uint_fast8_t: 1
uint_fast16_t: 4
uint_fast32_t: 4
uint_fast64_t: 8
intmax_t: 8
uintmax_t: 8
この実行結果は Visual Studio 2015 で試したものです。使っているコンパイラによって異なる可能性があります。
この実行結果の中でいえば、int_fast16_t、uint_fast16_t の大きさが注目で、それぞれ 4 になりました。これらの型は、16ビット以上の大きさを持ち、もっとも高速に演算が行える整数型を表しています(本編解説)。この処理系では、16ビットの整数型よりも、32ビットの整数型のほうが高速であることがうかがえます。
次の変数 n1~n8 の型はそれぞれ何になりますか?
signed char sc {-10};
unsigned short us {10};
auto n1 = 10L;
auto n2 = 10LL;
auto n3 = 10LU;
auto n4 = 10 + n1;
auto n5 = n2 + n3;
auto n6 = sc * 2;
auto n7 = sc * us;
auto n8 = sc + 100LL / 2U;
n1 の初期化子は 10L
です。整数リテラルに
Lサフィックスを付加した場合、long int になります(表現できない値なら
long long int や拡張整数型になるかもしれないが、10 は表現できる)(本編解説)。したがって、n1 の型は long int です。
n2 では 10LL
を指定しています。整数リテラルに
LLサフィックスを付加した場合、long long int
になります(表現できない値なら拡張整数型になるかもしれないが、10
は表現できる)(本編解説)。したがって、n2 の型は long long int です。
n3 では 10LU
を指定しています。LU
は、Lサフィックスと
Uサフィックスを両方指定したものです。Uサフィックスは符号無し整数型にすることを表しているので、n3 の型は unsigned long int です。
n4 では、10 + n1
を指定しています。10
は
int、n1 は long int でした。したがってこれは、int と long int
で計算を行っています。オペランドが2つある演算子で互いの型が異なる場合のルールが適用されます(本編解説)。「両方とも
signed、あるいは両方とも unsigned
である場合、ランクが低い側が、ランクが高い側の型に変換される」の部分が適用されるので、long
int に合わせられます。したがって、4 は long int
です。
n5 では、n2 + n3
を指定しています。n2 は long long
int、n3 は unsigned long int
です。さきほどと同様に一方の型に合わせる変換が起こります。long long int
が 64ビット、unsigned long int が 32ビットの処理系であれば、「signed
の側の型が、unsigned
の側の型で表現できるすべての値を表現できるのなら、unsigned の側が signed
の型に変換される」に該当するので、n5 は long long
int になります。long long int と unsigned long int がともに
64ビットの処理系であれば、「両方とも、signed の側の型に対応する unsigned
な型に変換される」に該当するので、n5 は unsigned long
long int になります。
n6 では、sc * 2
を指定しています。sc は signed
char、2
は int です。signed char は int
よりもランクが低いため、まず整数拡張が行われ(本編解説)、int
に変換されます。そのため、sc * 2
は int
同士での演算になり、その結果もまた int です。したがって、n6 は int です。
n7 では、sc * us
を指定しています。sc は signed char、us
は unsigned short です。さきほどと同様、それぞれに整数拡張が行われ、sc
は int になります。unsigned short からの整数拡張は、unsigned short
で表現できる値のすべてが int でも表現できるなら
int、表現できない値があるのなら unsigned int になります(本編解説)。したがって、unsigned
short が 16ビット、int が 32ビットの処理系ならば int
になります。いずれも 16ビットであるような処理系では unsigned int
になるでしょう。したがって、sc * us
は int と int
あるいは、int と unsigned int の乗算になります。前者なら n7 は int、後者なら「unsigned の側のランクが signed
の側のランク以上の場合、signed の側が unsigned
の側の型に変換される」が適用されるので、n7 は unsigned
int になります。
n8 では、sc + 100LL / 2U
を指定しています。sc は signed
char、100LL
は long long int、2U
は unsigned
int です。まず 100LL / 2U
が計算されます。long long int が
64ビット、unsigned int が 32ビットとすると、「signed
の側の型が、unsigned
の側の型で表現できるすべての値を表現できるのなら、unsigned の側が signed
の型に変換される」が適用されて、long long int
に合わせられます。sc
は整数拡張されて int になり、int と
long long int の加算を行うことになり、「両方とも
signed、あるいは両方とも unsigned
である場合、ランクが低い側が、ランクが高い側の型に変換される」が適用されるので、n8 は long long int です。もし、100LL / 2U
のとき、long long int と unsigned int が
64ビットであったとすれば、「両方とも、signed の側の型に対応する unsigned
な型に変換される」が適用され、両方とも unsigned long long int
に変換されます。そうなると、sc
との加算は int と unsigned
long long int の演算になるので、「unsigned の側のランクが signed
の側のランク以上の場合、signed の側が unsigned
の側の型に変換される」が適用され、n8 は unsigned long
long int になります。
トランプのマークをあらわす列挙型は、4通りの値が表現できれば十分なので、基底型を short や signed/unsigned char にできます。int の場合と比べて、メモリの使用量にどれだけ違いが出るでしょうか?
scoped enum では、デフォルトの基底型は int です。処理系によりますが、現代では int の大きさは 32ビットであることが多いです。short の大きさもまた処理系によりますが、多くの場合 16ビットです。signed char、unsigned char は 8ビットであると思われます。そのような処理系では、メモリ使用量は short にすれば半分に、signed char や unsigned char にすれば 4分の1 にまで減ると考えられます。
実用を考えると、この列挙型の変数が 52個ある可能性が高いでしょう(トランプ1セットの枚数。ジョーカーは除いた)。全体でのメモリ使用量は以下のようになります。
この削減効果に価値があるのかどうかは、プログラムの内容や、実行する環境で使えるメモリの容量などに左右されます。現代のほとんどの環境では、この程度のメモリ削減には意味がないかもしれません。
なお unscoped enum の場合、デフォルトの基底型は処理系定義で、定義したすべての列挙子の値を表現できる型が選ばれます。そのため、あえて short や signed char/unsigned char を指定しなくても、自動的に signed char/unsigned char 辺りが選択されている可能性はあります(Visual Studio 2015 では int でした)。
以下は、確認のためのプログラムです。
#include <iostream>
enum class CardMark1 {
,
spade,
club,
diamond,
heart};
enum class CardMark2 : short {
,
spade,
club,
diamond,
heart};
enum class CardMark3 : signed char {
,
spade,
club,
diamond,
heart};
enum CardMark4 {
,
spade,
club,
diamond,
heart};
int main()
{
std::cout << "sizeof(CardMark1) = " << sizeof(CardMark1) << "\n"
<< "sizeof(CardMark2) = " << sizeof(CardMark2) << "\n"
<< "sizeof(CardMark3) = " << sizeof(CardMark3) << "\n"
<< "sizeof(CardMark4) = " << sizeof(CardMark4) << "\n";
}
実行結果:
sizeof(CardMark1) = 4
sizeof(CardMark2) = 2
sizeof(CardMark3) = 1
sizeof(CardMark4) = 4
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |