【C++11】64ビット整数と文字列の相互変換

More than 1 year has passed since last update.

指摘により、例外について追記しました

はじまり

JSONで64ビット整数をやり取りしようと思ったら
JSON仕様では64ビット整数が存在しない!!
Number型にすれば52ビットまでは取れるけど
それ以上は取れない
JSONではバイナリデータも転送できない

よって、それらのケースは一般的に
数字を文字列に変換し、通信をすることが多いです

もちろん、バイナリデータを base64にして送ることも可能ではありますが
その場合には エンディアンや、内部表現形式といった問題も発生するので
あまり 環境依存するものはよくありませんね

int64_t、uint64_tから std::string への変換

streamを使うのも簡単にできますが、ここはお手軽に
std::to_string を使いましょう

to_string
#include <limits>
#include <iostream>
#include <string>
#include <cstdint>
template<typename T> using lim = std::numeric_limits<T>;
int main(){    
    using namespace std;
    cout
        << "std::numeric_limits<int64_t>::max()" << endl
        << lim<int64_t>::min() << endl
        << to_string(lim<int64_t>::min()) << endl;
    cout
        << "std::numeric_limits<int64_t>::max()" << endl
        << lim<int64_t>::max() << endl
        << to_string(lim<int64_t>::max()) << endl;
    cout
        << "std::numeric_limits<uint64_t>::max()" << endl
        << lim<uint64_t>::min() << endl
        << to_string(lim<uint64_t>::min()) << endl;
    cout
        << "std::numeric_limits<uint64_t>::max()" << endl
        << lim<uint64_t>::max() << endl
        << to_string(lim<uint64_t>::max()) << endl;

    return 0;
}
結果
std::numeric_limits<int64_t>::max()
-9223372036854775808
-9223372036854775808
std::numeric_limits<int64_t>::max()
9223372036854775807
9223372036854775807
std::numeric_limits<uint64_t>::max()
0
0
std::numeric_limits<uint64_t>::max()
18446744073709551615
18446744073709551615

大丈夫ですね!

std::stringから int64_t、uint64_tへ変換

まず

#include <type_traits>
#include <cstdint>
static_assert(std::is_same<long long, std::int64_t>::value, "err");
static_assert(std::is_same<unsigned long long, std::uint64_t>::value, "err");

がエラーにならないと仮定しないといけません。注意しましょう。

エラー処理を例外で行うケースは
signedの場合はstd::stoll、unsignedはstd::stoull

エラー処理をerrnoで行うケースは
signedの場合はstd::strtoll、unsignedはstd::strtoull

を使います

stoi系関数を使う

namespace std {
    long long stoll(const string& str, size_t* idx = 0, int base = 10);
    unsigned long long stoull(const string& str, size_t* idx = 0, int base = 10);
    long long stoll(const wstring& str, size_t* idx = 0, int base = 10);
    unsigned long long stoull(const wstring& str, size_t* idx = 0, int base = 10);
}
引数 説明
str 変換したい文字列
idx 変換できなかった最初の文字の要素番号を書き込む変数へのポインター。nullptrを指定すれば無視される。
base 何進数にするか
stoi系関数を使う
#include <limits>
#include <iostream>
#include <string>
template<typename T> using lim = std::numeric_limits<T>;
int main(){    
    using namespace std;
    using ll = long long;
    using ull = unsigned long long;

    cout
        << "std::numeric_limits<long long>::max()" << endl
        << lim<ll>::min() << endl
        << stoll(to_string(lim<ll>::min())) << endl;
    cout
        << "std::numeric_limits<long long>::max()" << endl
        << lim<ll>::max() << endl
        << stoll(to_string(lim<ll>::max())) << endl;
    cout
        << "std::numeric_limits<unsigned long long>::max()" << endl
        << lim<ull>::min() << endl
        << stoull(to_string(lim<ull>::min())) << endl;
    cout
        << "std::numeric_limits<unsigned long long>::max()" << endl
        << lim<ull>::max() << endl
        << stoull(to_string(lim<ull>::max())) << endl;

    return 0;
}

strtol系関数を使う

C99/11
long long int strtoll(const char * restrict nptr, char ** restrict endptr, int base);
unsigned long long int strtoull(const char * restrict nptr, char ** restrict endptr, int base);
引数 説明
nptr 変換したい文字列
endptr 変換できなかった最初の文字へのポインターを書き込む変数へのポインター。nullptrを指定すれば無視される。
base 何進数として解釈するか
strtol系関数を使う場合
#include <cstdlib>
#include <limits>
#include <iostream>
#include <string>
#include <cerrno>
template<typename T> using lim = std::numeric_limits<T>;
int main(){    
    using namespace std;
    using ll = long long;
    using ull = unsigned long long;

    cout << "std::numeric_limits<long long>::max()" << endl;
    cout << lim<ll>::min() << endl;
    {
        const auto s = to_string(lim<ll>::min());
        errno = 0;
        char* endptr;
        const auto re = strtoll(s.c_str(), &endptr, 10);
        if(0 != errno || (0 == re && endptr == s.c_str())) return -1;//error
        cout << re << endl;
    }

    cout << "std::numeric_limits<long long>::max()" << endl;
    cout << lim<ll>::max() << endl;
    {
        const auto s = to_string(lim<ll>::max());
        errno = 0;
        char* endptr;
        const auto re = strtoll(s.c_str(), &endptr, 10);
        if(0 != errno || (0 == re && endptr == s.c_str())) return -1;//error
        cout << re << endl;
    }

    cout << "std::numeric_limits<unsigned long long>::max()" << endl;
    cout << lim<ull>::min() << endl;
    {
        const auto s = to_string(lim<ull>::min());
        errno = 0;
        char* endptr;
        const auto re = strtoull(s.c_str(), &endptr, 10);
        if(0 != errno || (0 == re && endptr == s.c_str())) return -1;//error
        cout << re << endl;
    }

    cout << "std::numeric_limits<unsigned long long>::max()" << endl;
    cout << lim<ull>::max() << endl;
    {
        const auto s = to_string(lim<ull>::max());
        errno = 0;
        char* endptr;
        const auto re = strtoull(s.c_str(), &endptr, 10);
        if(0 != errno || (0 == re && endptr == s.c_str())) return -1;//error
        cout << re << endl;
    }

    return 0;
}

結果

結果
std::numeric_limits<long long>::max()
-9223372036854775808
-9223372036854775808
std::numeric_limits<long long>::max()
9223372036854775807
9223372036854775807
std::numeric_limits<unsigned long long>::max()
0
0
std::numeric_limits<unsigned long long>::max()
18446744073709551615
18446744073709551615

大丈夫ですね!