CPUには得意なデータ型というものがあります。
あるCPUでは4byteの数値計算が最も高効率に少ないクロック数で処理でき、また別のCPUでは8byteの数値計算が最も高効率に処理できる、といった具合です。
昔、世の中がまだ32bitCPUで溢れていた頃、ほとんどのCPUで最も高効率に計算できるデータ幅は32bit = 4byteでした。(あと8bit演算は常に高速でした)
その頃はintも往々にして4byteだったので、数値をやりとりする場合の引数、または戻り値の指定は単にintにしておけば良かったのです。
1 2 3 4 5 6 7 8 9 |
// 32bitCPUで高効率に処理できる可能性の高いコード int x2_fast(int arg) { return arg * arg; } // 32bitCPUで無駄な処理が発生する恐れの高いコード short x2_slow(unsigned char arg) { return arg * arg; } |
今でもそのころの名残で、慣例的に引数、戻り値をintで受ける書き方をしている人も多いのではないでしょうか。
時代は変わりました。
今やどこを見ても64bitCPUで溢れています。
にも関わらず、多くの環境でintは4byteのままです。
今の時代で引数に盲目的にintを指定することは、環境の変化に対応しきれていない可能性があります。
幸いにも、どの環境でもなるべく高効率になる数値型を標準が用意してくれています。
1 2 3 4 5 6 7 8 9 10 11 |
#include <cstdint> // 最速演算型はcstdintに定義されています std::int_fast8_t // 8bitの精度を保証した符号付き整数型 std::int_fast16_t // 16bitの精度を保証した符号付き整数型 std::int_fast32_t // 32bitの精度を保証した符号付き整数型 std::int_fast64_t // 64bitの精度を保証した符号付き整数型 std::uint_fast8_t // 8bitの精度を保証した符号無し整数型 std::uint_fast16_t // 16bitの精度を保証した符号無し整数型 std::uint_fast32_t // 32bitの精度を保証した符号無し整数型 std::uint_fast64_t // 64bitの精度を保証した符号無し整数型 |
fastの後ろの数値が最低限保証するbit精度です。
例えばuint_fast8_tは「最低でも8bitの精度が保証された中で最速のデータ幅」を持つ符号無し整数を表します。
0~100までの数値を引数で受け取って10倍にして返すという、(なんの使い道もない)関数を定義してみましょう。
1 2 3 4 5 6 |
#include <cstdint> std::uint_fast16_t // 戻り値は最大で1000が返るので最低でも16bitの精度が必要 x10(std::uint_fast8_t arg) { // 引数では0-100までの数値を受けるので8bitの精度が必要 return arg * 10; } |
精度が保証されているだけで8bit整数(std::uint8_t)と同じbit幅とは限らない点に注意して下さい。std::uint_fast8_tのデータ幅は16bitかもしれませんし、64bitかもしれません。
参考までに手元の環境における各型のサイズを示します。
int_fast8_t | 1byte |
uint_fast8_t | 1byte |
int_fast16_t | 8byte |
uint_fast16_t | 8byte |
int_fast32_t | 8byte |
uint_fast32_t | 8byte |
int_fast64_t | 8byte |
uint_fast64_t | 8byte |
まとめ
- cstdintに「Nbitの精度を保証して、かつ処理効率が最速になる型」というものが定義されている
- その型で保証している精度と実際のデータ幅は往々にして異なるのでデータ幅に依存したコードは書いてはいけない