浮動小数点数をテンプレートメタプログラミングで扱う

IEEE 754に準拠している倍精度浮動小数点数は符号が1ビット、指数部が11ビット、仮数部が1+52ビットという構造になっています。

```math
\begin{eqnarray}
x&=&(-1)^s\times 2^{e}\times 1.a_1a_2...a_{52}\\
&=&(-1)^s\times 2^{e}\times 1a_1a_2...a_{52}.0\times 2^{-52}\\
&=&(-1)^s\times 2^{e-52}\times 1a_1a_2...a_{52}.0
\end{eqnarray}
```

このように仮数部が64ビット以下の整数になるように指数部を調整して、元の値を`static constexpr`な値として保持するようにすると、double型をテンプレートメタプログラミングで扱うことができます。

http://melpon.org/wandbox/permlink/FJ9W1NkG6ouREIkM

```cpp:main.cpp
#include <iostream>

#include <cstdint>
#include <limits>
#include <tuple>
#include <type_traits>

#include <sprout/math/isnan.hpp>
#include <sprout/math/signbit.hpp>
#include <sprout/math/float2_exponent.hpp>
#include <sprout/math/isnormal.hpp>
#include <sprout/math/fabs.hpp>

namespace a{
    constexpr double pow2(int n)
    {
        double x = 1.0;
    
        if(n > 0){
            while(n-- != 0)
                x *= 2.0;
        }else if(n < 0){
            while(n++ != 0)
                x /= 2.0;
        }
    
        return x;
    }
    
    template <bool Negative, ::std::uint64_t Significand, int Exponent>
    struct double_{
        static constexpr double value =
            (Negative ? -1.0 : 1.0) * static_cast<double>(Significand) * pow2(Exponent);
    
        constexpr operator double() const
        {
            return value;
        }
    };
    
    template <bool Negative>
    struct zero{
        static constexpr double value = (Negative ? -0.0 : 0.0);
    
        constexpr operator double() const
        {
            return value;
        }
    };
    
    template <bool Negative>
    struct inf{
        static constexpr double value = Negative
           ? -::std::numeric_limits<double>::infinity()
           : ::std::numeric_limits<double>::infinity();
    
        constexpr operator double() const
        {
            return value;
        }
    };
    
    struct qnan{
        static constexpr double value = ::std::numeric_limits<double>::quiet_NaN();
    
        constexpr operator double() const
        {
            return value;
        }
    };
    
    struct snan{
        static constexpr double value = ::std::numeric_limits<double>::signaling_NaN();
    
        constexpr operator double() const
        {
            return value;
        }
    };
    
    enum class encode_type{
        number, zero, inf, qnan, snan
    };
    
    constexpr ::std::tuple<::std::uint64_t, int> encode_impl(double x)
    {
        x = ::sprout::fabs(x);
    
        if(x < 1.0){
            int i = 0;
    
            while(::std::int64_t(x) != x){
                x *= 2.0;
                i--;
            }
    
            return ::std::make_tuple(::std::uint64_t(x), i);
        }else{
            int i = 0;
            
            while(x > 1.0){
                x /= 2.0;
                i++;
            }
    
            return ::std::make_tuple(::std::uint64_t(x * pow2(53)), i - 53);
        }
    }
    
    constexpr ::std::tuple<encode_type, bool, ::std::uint64_t, int> encode(double x)
    {
        if(x == 0.0)
            return ::std::make_tuple(encode_type::zero, ::sprout::signbit(x), 0, 0);
        else if(x == inf<false>::value)
            return ::std::make_tuple(encode_type::inf, false, 0, 0);
        else if(x == inf<true>::value)
            return ::std::make_tuple(encode_type::inf, true, 0, 0);
        else if(::sprout::isnan(x))
            return ::std::make_tuple(encode_type::qnan, false, 0, 0);
        else
            return ::std::make_tuple(
                encode_type::number, x < 0.0,
                ::std::get<0>(encode_impl(x)),
                ::std::get<1>(encode_impl(x)));
    }
    
    template <encode_type T, bool Negative, ::std::uint64_t Significand, int Exponent>
    struct get_encoded_type{
        using type = double_<Negative, Significand, Exponent>;
    };
    
    template <bool Negative, ::std::uint64_t Significand, int Exponent>
    struct get_encoded_type<encode_type::zero, Negative, Significand, Exponent>{
        using type = zero<Negative>;
    };
    
    template <bool Negative, ::std::uint64_t Significand, int Exponent>
    struct get_encoded_type<encode_type::inf, Negative, Significand, Exponent>{
        using type = inf<Negative>;
    };
    
    template <bool Negative, ::std::uint64_t Significand, int Exponent>
    struct get_encoded_type<encode_type::qnan, Negative, Significand, Exponent>{
        using type = qnan;
    };
    
    template <bool Negative, ::std::uint64_t Significand, int Exponent>
    struct get_encoded_type<encode_type::snan, Negative, Significand, Exponent>{
        using type = snan;
    };
    
    template <encode_type T, bool Negative, ::std::uint64_t Significand, int Exponent>
    using get_encoded_type_t = typename get_encoded_type<T, Negative, Significand, Exponent>::type;
    
    template <typename T>
    struct is_encoded_double : ::std::false_type{
    };

    template <bool Negative, ::std::uint64_t Significand, int Exponent>
    struct is_encoded_double<double_<Negative, Significand, Exponent>>
        : ::std::true_type
    {
    };

    template <bool Negative>
    struct is_encoded_double<zero<Negative>> : ::std::true_type{
    };

    template <bool Negative>
    struct is_encoded_double<inf<Negative>> : ::std::true_type{
    };

    template <>
    struct is_encoded_double<qnan> : ::std::true_type{
    };

    template <>
    struct is_encoded_double<snan> : ::std::true_type{
    };

    template <typename T>
    constexpr bool is_encoded_double_v = is_encoded_double<T>::value;
}

#define D_T(x)\
    ::a::get_encoded_type_t<\
        ::std::get<0>(::a::encode(x)),\
        ::std::get<1>(::a::encode(x)),\
        ::std::get<2>(::a::encode(x)),\
        ::std::get<3>(::a::encode(x))>

#define D_(x) D_T(x){}

namespace a{
    template <
        typename T, typename U,
        ::std::enable_if_t<is_encoded_double_v<T> && is_encoded_double_v<U>>* = nullptr
    >
    constexpr auto operator+(T, U)
    {
        return D_(T::value + U::value);
    }
}

int main()
{
    constexpr auto x = D_(0.1234567890123456);
    constexpr auto y = D_(0.01234567890123456);
    
    std::cout.setf(std::ios::scientific);
    std::cout.precision(16);
    
    std::cout << 0.1234567890123456 + 0.01234567890123456 << std::endl;
    std::cout << decltype(x + y)::value << std::endl;
    std::cout << x + y << std::endl << std::endl;
    
    std::cout << std::numeric_limits<double>::denorm_min() << std::endl;
    std::cout << D_T(std::numeric_limits<double>::denorm_min())::value << std::endl << std::endl;
    
    std::cout << 1e100 << std::endl;
    std::cout << D_T(1e100)::value << std::endl << std::endl;
    
    std::cout << std::numeric_limits<double>::max() << std::endl;
    std::cout << D_T(std::numeric_limits<double>::max())::value << std::endl;
    
    return 0;
}

```

```shell-session:実行結果
1.3580246791358017e-01
1.3580246791358017e-01
1.3580246791358017e-01

4.9406564584124654e-324
4.9406564584124654e-324

1.0000000000000000e+100
1.0000000000000000e+100

1.7976931348623157e+308
1.7976931348623157e+308
```