LoginSignup
2
1

More than 5 years have passed since last update.

「非型テンプレートパラメータに浮動小数点数を渡す方法」をC++11に移植してみた

Last updated at Posted at 2019-03-24

https://qiita.com/kazatsuyu/items/1656e90b8ac0d999f12d をC++11環境でも使いたかったのでやってみた.

2019/03/27 追記

2の冪乗のとき operator* の結果が半分の値になるので現在修正中です.
従って, 現在の以下のコードは嘘がまぎれてます.

2019/03/27 追記2

修正. 凡ミス太郎...
<<= にしてました...

2019/03/28 追記

オリジナルの作者の kazatsuyu さんから公開の許可を頂いたのでCC0で取り敢えずgistに公開しました.
需要があるのか分かりませんが折角なのでC++11で使いたい方がいらっしゃれば.
https://gist.github.com/2bbb/254ed9627643c7359395b9155fe20c90

// original source by kazatsuyu
// https://qiita.com/kazatsuyu/items/1656e90b8ac0d999f12d
// https://github.com/kazatsuyu

#include <type_traits>
#include <cstdint>
#include <cstddef>
#include <numeric>

namespace alt_float {
    template <bool b, typename t, typename f>
    using conditional_t = typename std::conditional<b, t, f>::type;
}

namespace alt_float {
    template<typename ...Args>
    struct first_enabled {};

    template<typename T, typename ...Args>
    struct first_enabled<std::enable_if<true, T>, Args...> { using type = T; };
    template<typename T, typename ...Args>
    struct first_enabled<std::enable_if<false, T>, Args...>: first_enabled<Args...> {};
    template<typename T, typename ...Args>
    struct first_enabled<T, Args...> { using type = T; };

    template<typename ...Args>
    using first_enabled_t = typename first_enabled<Args...>::type;
}

namespace alt_float {
    enum class f32_t : std::uint32_t {};
    enum class f64_t : std::uint64_t {};
};

namespace alt_float {
    template<int n>
    using float_least_t = first_enabled_t<
        std::enable_if<std::numeric_limits<float>::digits >= n, float>,
        std::enable_if<std::numeric_limits<double>::digits >= n, double>,
        long double
    >;

    template<typename T>
    struct f_traits_base;

    template<>
    struct f_traits_base<f32_t>
    {
        using u_type = std::uint32_t;
        static constexpr auto fraction_bits = 23;
        static constexpr auto exponent_bits = 8;
    };
    template<>
    struct f_traits_base<f64_t>
    {
        using u_type = std::uint64_t;
        static constexpr auto fraction_bits = 52;
        static constexpr auto exponent_bits = 11;
    };

    namespace ldexp_detail {
        template <typename T>
        struct pair {
            const T x;
            const int n;
            constexpr pair(const T x, const int n)
            : x(x), n(n)
            {};
        };
        template <typename T>
        constexpr pair<T> while_positive(const pair<T> p) noexcept {
#define lshift64 ((uint64_t{} -1) + T{1.})
#define rshift64 (T{1.} / lshift64)
            return (64 <= p.n)
                ? while_positive(pair<T>{p.x * lshift64, p.n - 64})
                : p.n
                    ? pair<T>{p.x * (uint64_t{1} << p.n), p.n}
                    : p;
#undef lshift64
#undef rshift64
        }

        template <typename T>
        constexpr pair<T> while_negative(const pair<T> p) noexcept {
#define lshift64 ((uint64_t{} -1) + T{1.})
#define rshift64 (T{1.} / lshift64)
            return (p.n <= -64)
                ? while_negative(pair<T>{p.x * rshift64, p.n + 64})
                : p.n
                    ? pair<T>{p.x * rshift64 * 2 * (uint64_t{1} << (63 + p.n)), p.n}
                    : p;
#undef lshift64
#undef rshift64
        }
    };
    template<typename T>
    constexpr T ldexp(T x, int n) noexcept {
#define lshift64 ((uint64_t{} -1) + T{1.})
#define rshift64 (T{1.} / lshift64)
        return (0 <= n)
            ? ldexp_detail::while_positive(ldexp_detail::pair<T>{x, n}).x
            : ldexp_detail::while_negative(ldexp_detail::pair<T>{x, n}).x;
#undef lshift64
#undef rshift64
    }

    template<typename T>
    constexpr T pow2(int n) noexcept { return ldexp(T{1.}, n); }

    template<typename T>
    struct f_traits
    {
        using base = f_traits_base<T>;
        using u_type = typename base::u_type; // 同一サイズの符号なし整数型
        static constexpr auto fraction_bits = base::fraction_bits; // IEEE 754の内部表現における基数部分のビット数
        static constexpr auto exponent_bits = base::exponent_bits; // IEEE 754の内部表現における指数部分のビット数
        using f_type = float_least_t<fraction_bits+1>; // 少なくとも(fraction+1)bitの精度を持つ浮動小数点数型
        static constexpr auto fraction_mask = (u_type{1} << fraction_bits) -1; // and演算で基数部のみを取り出すためのマスク
        static constexpr auto exponent_mask = (u_type{1} << exponent_bits) -1; // and演算で指数部のみを取り出すためのマスク
        static constexpr auto sign_mask = u_type{1} << (fraction_bits + exponent_bits); // and演算で符号部のみを取り出すためのマスク
        static constexpr auto bias = (1 << (exponent_bits-1))-1; // 指数部のバイアス
        static constexpr auto denorm_min = pow2<f_type>(-fraction_bits-bias+1); // 最小の非正規化数の値
        static constexpr auto norm_min = pow2<f_type>(-bias+1);
    };
}

namespace alt_float {
    template<bool cond>
    using enable_when = typename std::enable_if<cond, std::nullptr_t>::type;

    template<typename T>
    struct is_alt_float : conditional_t<std::is_same<T, f32_t>{} || std::is_same<T, f64_t>{}, std::true_type, std::false_type> {};

    template<typename T, typename U>
    struct is_compatible : std::false_type {};
    template<typename T>
    struct is_compatible<T, f32_t> : conditional_t<std::is_same<T, typename f_traits<f32_t>::f_type>{}, std::true_type, std::false_type> {};
    template<typename T>
    struct is_compatible<T, f64_t> : conditional_t<std::is_same<T, typename f_traits<f64_t>::f_type>{}, std::true_type, std::false_type> {};

    namespace cast_detail {
        template <typename T, typename U>
        struct pair {
            const U value;
            const decltype(f_traits<T>::bias + f_traits<T>::fraction_bits) e;
            constexpr pair(U value, decltype(e) e)
            : e(e), value(value) {};
        };
        template <typename T, typename U>
        constexpr pair<T, U> while1(const pair<T, U> p) {
            return p.value >= pow2<U>(f_traits<T>::fraction_bits + 1)
                ? while1<T, U>(pair<T, U>{p.value * static_cast<U>(0.5), p.e + 1})
                : p;
        }
        template <typename T, typename U>
        constexpr pair<T, U> while2(const pair<T, U> p) {
            return p.value < pow2<U>(f_traits<T>::fraction_bits)
                ? while2<T, U>(pair<T, U>{p.value * static_cast<U>(2), p.e - 1})
                : p;
        }
        template <typename T, typename U>
        constexpr pair<T, U> while_mod(const pair<T, U> p)
        { return while2<T, U>(while1<T, U>(p)); }
    }

    template<typename T, typename U, enable_when<is_compatible<U, T>::value> = nullptr>
    constexpr T cast(U value) noexcept {
        using t = f_traits<T>;
        using f_type = typename t::f_type;
        using u_type = typename t::u_type;
#define fb (t::fraction_bits)
#define fm (t::fraction_mask)
#define em (t::exponent_mask)
#define sm (t::sign_mask)
#define nm (t::norm_min)
#define bi (t::bias)
#define sign (value < 0)
#define signed_value ((sign ? -1 : 1) * value)
#define RETURN(...) (static_cast<T>(sign ? sm | (__VA_ARGS__) : (__VA_ARGS__)))
        return (value != value)
            ? static_cast<T>((em << fb) | fm)
            : (signed_value == std::numeric_limits<f_type>::infinity())
                ? RETURN(em << fb)
                : (signed_value < nm)
                    ? RETURN(static_cast<u_type>(signed_value * pow2<U>(fb + bi - 1)))
                    : RETURN(
                        (
                            static_cast<u_type>(
                                cast_detail::while_mod<T, U>(
                                    cast_detail::pair<T, U>{signed_value, bi + fb}
                                ).e
                            )
                            << fb
                        ) | (
                            static_cast<u_type>(
                                cast_detail::while_mod<T, U>(
                                    cast_detail::pair<T, U>{signed_value, bi + fb}
                                ).value
                            ) & fm
                        )
                    );
#undef fb
#undef fm
#undef em
#undef sm
#undef nm
#undef bi
#undef sign
#undef signed_value
#undef RETURN
    }

    template<typename T, typename U, enable_when<is_alt_float<T>::value && !is_alt_float<U>::value && !is_compatible<U, T>::value> = nullptr>
    constexpr T cast(U value) noexcept(noexcept(static_cast<typename f_traits<T>::f_type>(value))) {
        return cast<T>(static_cast<typename f_traits<T>::f_type>(value));
    }

    template<typename T, typename U, enable_when<is_compatible<T, U>::value> = nullptr>
    constexpr T cast(U value) noexcept {
        using t = f_traits<U>;
        using u_type = typename t::u_type;
        using limits = std::numeric_limits<T>;

#define fb (t::fraction_bits)
#define fm (t::fraction_mask)
#define em (t::exponent_mask)
#define sm (t::sign_mask)
#define bi (t::bias)
#define dm (t::denorm_min)

#define s (!!(static_cast<u_type>(value) & sm))
#define e ((static_cast<u_type>(value) >> fb) & em)
#define f (static_cast<u_type>(value) & fm)
#define RETURN(...) (s ? -(__VA_ARGS__) : __VA_ARGS__)
        return (e == em)
            ? (!f)
                ? RETURN(limits::infinity())
                    : (f & (u_type{1} << (fb - 1)))
                        ? RETURN(limits::quiet_NaN())
                        : RETURN(limits::signaling_NaN())
            : (!e)
                ? RETURN(f * dm)
                : RETURN(ldexp<T>(f | (u_type{1} << fb), e - fb - bi));
#undef fb
#undef fm
#undef em
#undef sm
#undef bi
#undef dm

#undef s
#undef e
#undef f
#undef RETURN
    }
    template<typename T, typename U, enable_when<is_alt_float<U>::value && !is_alt_float<T>::value && !is_compatible<T, U>::value> = nullptr>
    constexpr T cast(U value) noexcept(noexcept(static_cast<T>(typename f_traits<U>::f_type{}))) {
        return static_cast<T>(cast<typename f_traits<U>::f_type>(value));
    }

    template<typename T, typename U, enable_when<is_alt_float<U>::value && is_alt_float<T>::value> = nullptr>
    constexpr T cast(U value) noexcept {
        return cast<T>(cast<typename f_traits<U>::f_type>(value));
    }
}

namespace alt_float {
    inline namespace operators {
        template<typename T, typename U> struct common_type : std::common_type<T, U> {};
        template<> struct common_type<f32_t, f64_t> { using type = f64_t; };
        template<> struct common_type<f64_t, f32_t> { using type = f64_t; };
        template<typename T, typename U>
        using common_type_t = typename common_type<T, U>::type;

        template<typename T, enable_when<is_alt_float<T>::value> = nullptr>
        constexpr T operator -(T value) noexcept {
            return value ^ f_traits<T>::sign_mask;
        }
        template<typename T, typename U, enable_when<is_alt_float<T>::value && is_alt_float<U>::value> = nullptr>
        constexpr auto operator +(T l, U r) noexcept
            -> decltype(cast<common_type_t<T, U>>(cast<typename f_traits<T>::f_type>(l) + cast<typename f_traits<U>::f_type>(r)))
        {
            using f1 = typename f_traits<T>::f_type;
            using f2 = typename f_traits<U>::f_type;
            return cast<common_type_t<T, U>>(cast<f1>(l) + cast<f2>(r));
        }
        template<typename T, typename U, enable_when<is_alt_float<T>::value && !is_alt_float<U>::value> = nullptr>
        constexpr T operator +(T l, U r) noexcept(noexcept(cast<T>(r))) {
            using f_type = typename f_traits<T>::f_type;
            return cast<T>(cast<f_type>(l) + static_cast<f_type>(r));
        }
        template<typename T, typename U, enable_when<!is_alt_float<T>::value && is_alt_float<U>::value> = nullptr>
        constexpr U operator +(T l, U r) noexcept(noexcept(r + l)) { return r + l; }
        template<typename T, typename U, enable_when<is_alt_float<T>::value && is_alt_float<U>::value> = nullptr>
        constexpr auto operator -(T l, U r) noexcept
            -> decltype(cast<common_type_t<T, U>>(cast<typename f_traits<T>::f_type>(l) - cast<typename f_traits<U>::f_type>(r)))
        {
            using f1 = typename f_traits<T>::f_type;
            using f2 = typename f_traits<U>::f_type;
            return cast<common_type_t<T, U>>(cast<f1>(l) - cast<f2>(r));
        }
        template<typename T, typename U, enable_when<is_alt_float<T>::value && !is_alt_float<U>::value> = nullptr>
        constexpr T operator -(T l, U r) noexcept(noexcept(cast<T>(r))) {
            using f_type = typename f_traits<T>::f_type;
            return cast<T>(cast<f_type>(l) - static_cast<f_type>(r));
        }
        template<typename T, typename U, enable_when<!is_alt_float<T>::value && is_alt_float<U>::value> = nullptr>
        constexpr U operator -(T l, U r) noexcept(noexcept(cast<U>(l))) {
            using f_type = typename f_traits<U>::f_type;
            return cast<U>(static_cast<f_type>(l) - cast<f_type>(r));
        }
        template<typename T, typename U, enable_when<is_alt_float<T>::value && is_alt_float<U>::value> = nullptr>
        constexpr auto operator *(T l, U r) noexcept
            -> decltype(cast<common_type_t<T, U>>(cast<typename f_traits<T>::f_type>(l) * cast<typename f_traits<U>::f_type>(r)))
        {
            using f1 = typename f_traits<T>::f_type;
            using f2 = typename f_traits<U>::f_type;
            return cast<common_type_t<T, U>>(cast<f1>(l) * cast<f2>(r));
        }
        template<typename T, typename U, enable_when<is_alt_float<T>::value && !is_alt_float<U>::value> = nullptr>
        constexpr T operator *(T l, U r) noexcept(noexcept(cast<T>(r))) {
            using f_type = typename f_traits<T>::f_type;
            return cast<T>(cast<f_type>(l) * static_cast<f_type>(r));
        }
        template<typename T, typename U, enable_when<!is_alt_float<T>::value && is_alt_float<U>::value> = nullptr>
        constexpr U operator *(T l, U r) noexcept(noexcept(r * l)) { return r * l; }
        template<typename T, typename U, enable_when<is_alt_float<T>::value && is_alt_float<U>::value> = nullptr>
        constexpr auto operator /(T l, U r) noexcept
            -> decltype(cast<common_type_t<T, U>>(cast<typename f_traits<T>::f_type>(l) / cast<typename f_traits<U>::f_type>(r)))
        {
            using f1 = typename f_traits<T>::f_type;
            using f2 = typename f_traits<U>::f_type;
            return cast<common_type_t<T, U>>(cast<f1>(l) / cast<f2>(r));
        }
        template<typename T, typename U, enable_when<is_alt_float<T>::value && !is_alt_float<U>::value> = nullptr>
        constexpr T operator /(T l, U r) noexcept(noexcept(cast<T>(r))) {
            using f_type = typename f_traits<T>::f_type;
            return cast<T>(cast<f_type>(l) / static_cast<f_type>(r));
        }
        template<typename T, typename U, enable_when<!is_alt_float<T>::value && is_alt_float<U>::value> = nullptr>
        constexpr U operator /(T l, U r) noexcept(noexcept(cast<U>(l))) {
            using f_type = typename f_traits<U>::f_type;
            return cast<U>(static_cast<f_type>(l) / cast<f_type>(r));
        }
        template<typename T, enable_when<is_alt_float<T>::value> = nullptr>
        constexpr auto operator *(T value) noexcept
            -> decltype(cast<typename f_traits<T>::f_type>(value))
        {
            return cast<typename f_traits<T>::f_type>(value);
        }
    }
}

namespace alt_float {
    inline namespace literal {
        constexpr f32_t operator ""_f32(long double f) noexcept { return cast<f32_t>(f); }
        constexpr f32_t operator ""_f32(unsigned long long f) noexcept { return cast<f32_t>(static_cast<long double>(f)); }
        constexpr f64_t operator ""_f64(long double f) noexcept { return cast<f64_t>(f); }
        constexpr f64_t operator ""_f64(unsigned long long f) noexcept { return cast<f64_t>(static_cast<long double>(f)); }
    }
}

C++14って便利なんだなー...
ローカル変数は(負けた気分になりながら)マクロでなんとかするとしても, 複数の変数を書き換えるような while とかを外に出すとなると constexpr付きのコンストラクタを持った std::pair 的な何かを自分で書いてごにょごにょ...

取り敢えず動いたし良しとしよう.

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1