232 lines
5.3 KiB
C++
232 lines
5.3 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <concepts>
|
|
|
|
#include "div.hpp"
|
|
|
|
struct fp_raw_tag {};
|
|
|
|
template <typename T, int B>
|
|
constexpr inline int fp_fractional(std::floating_point auto n)
|
|
{
|
|
return (n - static_cast<T>(n)) * (1 << B);
|
|
}
|
|
|
|
template <typename T, int B>
|
|
constexpr inline int fp_integral(std::floating_point auto n)
|
|
{
|
|
return static_cast<T>(n) << B;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
struct fp
|
|
{
|
|
T value;
|
|
|
|
constexpr inline fp(std::integral auto n)
|
|
: value(n * (1 << B))
|
|
{}
|
|
|
|
consteval inline fp(std::floating_point auto n)
|
|
: value(fp_integral<T, B>(n) + fp_fractional<T, B>(n))
|
|
{}
|
|
|
|
constexpr inline explicit fp(T n, struct fp_raw_tag)
|
|
: value(n)
|
|
{}
|
|
|
|
constexpr inline explicit operator int() const
|
|
{
|
|
return value >> 16;
|
|
}
|
|
|
|
constexpr inline fp<T, I, B> operator-()
|
|
{
|
|
return fp(-value, fp_raw_tag{});
|
|
}
|
|
|
|
inline constexpr fp<T, I, B>& operator=(fp<T, I, B> const& v);
|
|
|
|
inline constexpr fp<T, I, B>& operator+=(fp<T, I, B> const& v);
|
|
|
|
inline constexpr fp<T, I, B>& operator-=(fp<T, I, B> const& v);
|
|
|
|
inline constexpr fp<T, I, B>& operator*=(fp<T, I, B> const& v);
|
|
};
|
|
|
|
template <typename T, typename I, int B>
|
|
inline constexpr fp<T, I, B>& fp<T, I, B>::operator=(fp<T, I, B> const& v)
|
|
{
|
|
this->value = v.value;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
inline constexpr fp<T, I, B>& fp<T, I, B>::operator+=(fp<T, I, B> const& v)
|
|
{
|
|
*this = *this + v;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
inline constexpr fp<T, I, B>& fp<T, I, B>::operator-=(fp<T, I, B> const& v)
|
|
{
|
|
*this = *this - v;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
inline constexpr fp<T, I, B>& fp<T, I, B>::operator*=(fp<T, I, B> const& v)
|
|
{
|
|
*this = *this * v;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> operator+(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
return fp<T, I, B>(a.value + b.value, fp_raw_tag{});
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> operator>>(const fp<T, I, B>& a, int s)
|
|
{
|
|
return fp<T, I, B>(a.value >> s, fp_raw_tag{});
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> operator<<(const fp<T, I, B>& a, int s)
|
|
{
|
|
return fp<T, I, B>(a.value << s, fp_raw_tag{});
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> operator-(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
return fp<T, I, B>(a.value - b.value, fp_raw_tag{});
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> operator*(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
const I p = (static_cast<I>(a.value) * static_cast<I>(b.value));
|
|
return fp<T, I, B>(static_cast<T>(p >> B), fp_raw_tag{});
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> operator*(const fp<T, I, B>& a, T b)
|
|
{
|
|
I p = (static_cast<I>(a.value) * static_cast<I>(b));
|
|
return fp<T, I, B>(static_cast<T>(p), fp_raw_tag{});
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> operator*(T b, const fp<T, I, B>& a)
|
|
{
|
|
I p = (static_cast<I>(a.value) * static_cast<I>(b));
|
|
return fp<T, I, B>(static_cast<T>(p), fp_raw_tag{});
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> operator/(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
//T p = (static_cast<T>(a.value) * ) / static_cast<T>(b.value);
|
|
//T p = static_cast<T>(a.value) / static_cast<T>(b.value);
|
|
I p = __div64_32((static_cast<I>(a.value) << 16), static_cast<T>(b.value));
|
|
|
|
return fp<T, I, B>(static_cast<T>(p), fp_raw_tag{});
|
|
}
|
|
|
|
// comparison
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline bool operator==(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
return a.value == b.value;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline bool operator!=(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
return a.value != b.value;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline bool operator<(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
return a.value < b.value;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline bool operator>(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
return a.value > b.value;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline bool operator<=(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
return a.value <= b.value;
|
|
}
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline bool operator>=(const fp<T, I, B>& a, const fp<T, I, B>& b)
|
|
{
|
|
return a.value >= b.value;
|
|
}
|
|
|
|
// limits
|
|
|
|
template <typename F>
|
|
struct fp_limits;
|
|
|
|
template <typename T, typename I, int B>
|
|
struct fp_limits<fp<T, I, B>>
|
|
{
|
|
static constexpr fp<T, I, B> min()
|
|
{
|
|
return fp<T, I, B>(-(1 << (2 * B - 1)), fp_raw_tag{});
|
|
}
|
|
|
|
static constexpr fp<T, I, B> max()
|
|
{
|
|
return fp<T, I, B>((static_cast<I>(1) << (2 * B - 1)) - 1, fp_raw_tag{});
|
|
}
|
|
};
|
|
|
|
// functions
|
|
|
|
template <typename T, typename I, int B>
|
|
constexpr inline fp<T, I, B> pow(fp<T, I, B> a, fp<T, I, B> b)
|
|
{
|
|
while (b > fp<T, I, B>(1)) {
|
|
a *= a;
|
|
b -= fp<T, I, B>(1);
|
|
}
|
|
return a;
|
|
}
|
|
|
|
// specializations
|
|
|
|
using fp16_16 = fp<int32_t, int64_t, 16>;
|
|
|
|
constexpr inline fp16_16 sqrt(const fp16_16& n)
|
|
{
|
|
int32_t x = n.value;
|
|
int32_t c = 0;
|
|
int32_t d = 1 << 30;
|
|
while (d > (1 << 6))
|
|
{
|
|
int32_t t = c + d;
|
|
if (x >= t)
|
|
{
|
|
x -= t;
|
|
c = t + d;
|
|
}
|
|
x <<= 1;
|
|
d >>= 1;
|
|
}
|
|
return fp16_16(c >> 8, fp_raw_tag{});
|
|
}
|