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
的な何かを自分で書いてごにょごにょ...
取り敢えず動いたし良しとしよう.