User-defined literals で固定長の Boost.Multiprecision を生成の続き

昨日の続き
昨日とは違い、その数値が収まる 2のn乗のビット幅を使用するようにしてみました。

[ソース]

#include <boost/multiprecision/cpp_int.hpp>

template<char ...cs>
struct chars{};

constexpr bool
operator <=(chars<>, chars<>){
    return true;
}

template<char ...cs>
constexpr bool
operator <=(chars<>, chars<cs...>){
    return true;
}

template<char ...cs>
constexpr bool
operator <=(chars<cs...>, chars<>){
    return false;
}


template<char top1, char ...cs1, char top2, char ...cs2>
constexpr bool
operator <=(chars<top1, cs1...>, chars<top2, cs2...>){
    return sizeof...(cs1) < sizeof...(cs2) ? true
         : sizeof...(cs1) > sizeof...(cs2) ? false
         : top1 < top2 ? true
         : top1 > top2 ? false
         : chars<cs1...>{} <= chars<cs2...>{};
}


template<char ...cs>
constexpr chars<cs...>
operator "" _chars(){
    return {};
}

template<typename Precision>
constexpr std::size_t
bit(Precision&& rhs){
    return rhs <= 127_chars ? 8
         : rhs <= 32767_chars ? 16
         : rhs <= 2147483647_chars ? 32
         : rhs <= 9223372036854775807_chars ? 64
         : rhs <= 170141183460469231731687303715884105727_chars ? 128
         : rhs <= 57896044618658097711785492504343953926634992332820282019728792003956564819967_chars ? 256
         : rhs <= 6703903964971298549787012499102923063739682910296196688861780721860882015036773488400937149083451713845015929093243025426876941405973284973216824503042047_chars ? 512
         : rhs <= 89884656743115795386465259539451236680898848947115328636715040578866337902750481566354238661203768010560056939935696678829394884407208311246423715319737062188883946712432742638151109800623047059726541476042502884419075341171231440736956555270413618581675255342293149119973622969239858152417678164812112068607_chars ? 1024
         : 2048;
}


template<int Precision>
using cpp_int = boost::multiprecision::number<
    boost::multiprecision::cpp_int_backend<
        Precision,
        Precision,
        boost::multiprecision::signed_magnitude,
        boost::multiprecision::unchecked,
        void
    >
>;


template<char ...cs>
cpp_int<bit(chars<cs...>{})>
operator "" _cpp_int(){
    char const str[] = { cs..., '\0' };
    return cpp_int<bit(chars<cs...>{})>(str);
}


#include <iostream>
#include <limits>

int
main(){
    auto hoge = 432984_cpp_int;
    std::cout << hoge << std::endl;
    std::cout << std::numeric_limits<decltype(hoge)>::digits << std::endl;

    auto n = 573147844013817084101_cpp_int;
    std::cout << n << std::endl;
    std::cout << std::numeric_limits<decltype(n)>::digits << std::endl;

    auto m = 927372692193078999176_cpp_int;
    std::cout << m << std::endl;
    std::cout << std::numeric_limits<decltype(m)>::digits << std::endl;

    auto o = n + m;
    std::cout << o << std::endl;

    auto cpp128 = 170141183460469231731687303715884105727_cpp_int;
    std::cout << cpp128 << std::endl;
    std::cout << std::numeric_limits<decltype(cpp128)>::digits << std::endl;

    auto cpp256 = 57896044618658097711785492504343953926634992332820282019728792003956564819967_cpp_int;
    std::cout << cpp256 << std::endl;
    std::cout << std::numeric_limits<decltype(cpp256)>::digits << std::endl;

    return 0;
}

[出力]

432984
32
573147844013817084101
128
927372692193078999176
128
1500520536206896083277
170141183460469231731687303715884105727
128
57896044618658097711785492504343953926634992332820282019728792003956564819967
256


まぁ毎回 int128_t や int256_t を使用するよりはマシかな。
桁が大きすぎるとどの型を使えばいいのかすぐにはわからないだろうし、そういう意味では利便性がありそうな気がしないでもない。
(もっとも、実際に数値リテラルから固定長の Boost.Multiprecision を生成する場合は、長さを決め打ちで使いそうですが。


あと余談ですが数値が大きすぎてそのままではコンパイル時に比較ができないので文字として値を比較しています。
User-defined literals を使ったら思ったよりも便利だった。
constexpr ばんざーい。

[boost]

  • ver 1.53.0

[コンパイラ]

  • g++ (GCC) 4.8.0 20130210 (experimental)