Fixed-point improvements:
* Mark everything constexpr * Use unsigned type for multiplication to prevent UB on overflow * Add fp -> fp conversions * Add floor & ceil functions
This commit is contained in:
parent
7beba986ae
commit
8a739bf637
1 changed files with 90 additions and 39 deletions
|
|
@ -106,7 +106,7 @@ namespace psemek::util
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename H>
|
template <typename T, typename H>
|
||||||
H clamp(T x, H min, H max)
|
constexpr H clamp(T x, H min, H max)
|
||||||
{
|
{
|
||||||
if (x < static_cast<T>(min))
|
if (x < static_cast<T>(min))
|
||||||
return min;
|
return min;
|
||||||
|
|
@ -147,32 +147,37 @@ namespace psemek::util
|
||||||
using unsigned_rep_type = typename traits::unsigned_rep_type;
|
using unsigned_rep_type = typename traits::unsigned_rep_type;
|
||||||
using unsigned_wide_type = typename traits::unsigned_wide_type;
|
using unsigned_wide_type = typename traits::unsigned_wide_type;
|
||||||
|
|
||||||
fixed_point() = default;
|
constexpr fixed_point() = default;
|
||||||
fixed_point(fixed_point const &) = default;
|
constexpr fixed_point(fixed_point const &) = default;
|
||||||
|
|
||||||
|
constexpr fixed_point & operator = (fixed_point const &) = default;
|
||||||
|
|
||||||
template <std::integral T>
|
template <std::integral T>
|
||||||
explicit fixed_point(T value);
|
constexpr explicit fixed_point(T value);
|
||||||
|
|
||||||
template <std::floating_point T>
|
template <std::floating_point T>
|
||||||
explicit fixed_point(T value);
|
constexpr explicit fixed_point(T value);
|
||||||
|
|
||||||
|
template <unsigned int I2, unsigned int F2, bool S2>
|
||||||
|
constexpr explicit fixed_point(fixed_point<I2, F2, S2> value);
|
||||||
|
|
||||||
template <std::integral T>
|
template <std::integral T>
|
||||||
explicit operator T () const;
|
constexpr explicit operator T () const;
|
||||||
|
|
||||||
template <std::floating_point T>
|
template <std::floating_point T>
|
||||||
explicit operator T () const;
|
constexpr explicit operator T () const;
|
||||||
|
|
||||||
fixed_point & operator += (fixed_point);
|
constexpr fixed_point & operator += (fixed_point);
|
||||||
fixed_point & operator -= (fixed_point);
|
constexpr fixed_point & operator -= (fixed_point);
|
||||||
fixed_point & operator *= (fixed_point);
|
constexpr fixed_point & operator *= (fixed_point);
|
||||||
fixed_point & operator /= (fixed_point);
|
constexpr fixed_point & operator /= (fixed_point);
|
||||||
|
|
||||||
rep_type rep() const { return rep_; }
|
constexpr rep_type rep() const { return rep_; }
|
||||||
|
|
||||||
static fixed_point from_rep(rep_type rep);
|
static constexpr fixed_point from_rep(rep_type rep);
|
||||||
|
|
||||||
static fixed_point min();
|
static constexpr fixed_point min();
|
||||||
static fixed_point max();
|
static constexpr fixed_point max();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
rep_type rep_;
|
rep_type rep_;
|
||||||
|
|
@ -180,32 +185,47 @@ namespace psemek::util
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
template <std::integral T>
|
template <std::integral T>
|
||||||
fixed_point<I, F, S>::fixed_point(T value)
|
constexpr fixed_point<I, F, S>::fixed_point(T value)
|
||||||
: rep_(static_cast<rep_type>(value) << F)
|
: rep_(static_cast<rep_type>(value) << F)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
template <std::floating_point T>
|
template <std::floating_point T>
|
||||||
fixed_point<I, F, S>::fixed_point(T value)
|
constexpr fixed_point<I, F, S>::fixed_point(T value)
|
||||||
: rep_(static_cast<rep_type>(detail::clamp(std::round(value * (static_cast<rep_type>(1) << F)), min().rep(), max().rep())))
|
: rep_(static_cast<rep_type>(detail::clamp(std::round(value * (static_cast<rep_type>(1) << F)), min().rep(), max().rep())))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
|
template <unsigned int I2, unsigned int F2, bool S2>
|
||||||
|
constexpr fixed_point<I, F, S>::fixed_point(fixed_point<I2, F2, S2> value)
|
||||||
|
{
|
||||||
|
// TODO: what to do with signs?
|
||||||
|
if constexpr (F >= F2)
|
||||||
|
{
|
||||||
|
rep_ = static_cast<rep_type>(value.rep_) << (F - F2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rep_ = static_cast<rep_type>(value.rep_) >> (F - F2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
template <std::integral T>
|
template <std::integral T>
|
||||||
fixed_point<I, F, S>::operator T () const
|
constexpr fixed_point<I, F, S>::operator T () const
|
||||||
{
|
{
|
||||||
return static_cast<T>(rep_) >> F;
|
return static_cast<T>(rep_) >> F;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
template <std::floating_point T>
|
template <std::floating_point T>
|
||||||
fixed_point<I, F, S>::operator T () const
|
constexpr fixed_point<I, F, S>::operator T () const
|
||||||
{
|
{
|
||||||
return rep_ / static_cast<T>(static_cast<rep_type>(1) << F);
|
return rep_ / static_cast<T>(static_cast<rep_type>(1) << F);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> fixed_point<I, F, S>::from_rep(fixed_point<I, F, S>::rep_type rep)
|
constexpr fixed_point<I, F, S> fixed_point<I, F, S>::from_rep(fixed_point<I, F, S>::rep_type rep)
|
||||||
{
|
{
|
||||||
fixed_point<I, F, S> result;
|
fixed_point<I, F, S> result;
|
||||||
result.rep_ = rep;
|
result.rep_ = rep;
|
||||||
|
|
@ -213,7 +233,7 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> fixed_point<I, F, S>::min()
|
constexpr fixed_point<I, F, S> fixed_point<I, F, S>::min()
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
|
|
@ -221,7 +241,7 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> fixed_point<I, F, S>::max()
|
constexpr fixed_point<I, F, S> fixed_point<I, F, S>::max()
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
|
|
@ -229,7 +249,7 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> operator - (fixed_point<I, F, S> x)
|
constexpr fixed_point<I, F, S> operator - (fixed_point<I, F, S> x)
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
|
|
@ -238,7 +258,7 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> operator + (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
constexpr fixed_point<I, F, S> operator + (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
|
|
@ -247,7 +267,7 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> operator - (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
constexpr fixed_point<I, F, S> operator - (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
|
|
@ -256,16 +276,16 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> operator * (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
constexpr fixed_point<I, F, S> operator * (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
using wide_type = typename FP::wide_type;
|
using unsigned_wide_type = typename FP::unsigned_wide_type;
|
||||||
return FP::from_rep(static_cast<rep_type>((static_cast<wide_type>(x1.rep()) * static_cast<wide_type>(x2.rep())) >> F));
|
return FP::from_rep(static_cast<rep_type>((static_cast<unsigned_wide_type>(x1.rep()) * static_cast<unsigned_wide_type>(x2.rep())) >> F));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> operator / (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
constexpr fixed_point<I, F, S> operator / (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
|
|
@ -274,47 +294,47 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> & fixed_point<I, F, S>::operator += (fixed_point<I, F, S> x)
|
constexpr fixed_point<I, F, S> & fixed_point<I, F, S>::operator += (fixed_point<I, F, S> x)
|
||||||
{
|
{
|
||||||
(*this) = (*this) + x;
|
(*this) = (*this) + x;
|
||||||
return (*this);
|
return (*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> & fixed_point<I, F, S>::operator -= (fixed_point<I, F, S> x)
|
constexpr fixed_point<I, F, S> & fixed_point<I, F, S>::operator -= (fixed_point<I, F, S> x)
|
||||||
{
|
{
|
||||||
(*this) = (*this) - x;
|
(*this) = (*this) - x;
|
||||||
return (*this);
|
return (*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> & fixed_point<I, F, S>::operator *= (fixed_point<I, F, S> x)
|
constexpr fixed_point<I, F, S> & fixed_point<I, F, S>::operator *= (fixed_point<I, F, S> x)
|
||||||
{
|
{
|
||||||
(*this) = (*this) * x;
|
(*this) = (*this) * x;
|
||||||
return (*this);
|
return (*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> & fixed_point<I, F, S>::operator /= (fixed_point<I, F, S> x)
|
constexpr fixed_point<I, F, S> & fixed_point<I, F, S>::operator /= (fixed_point<I, F, S> x)
|
||||||
{
|
{
|
||||||
(*this) = (*this) / x;
|
(*this) = (*this) / x;
|
||||||
return (*this);
|
return (*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
bool operator == (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
constexpr bool operator == (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
||||||
{
|
{
|
||||||
return x1.rep() == x2.rep();
|
return x1.rep() == x2.rep();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
auto operator <=> (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
constexpr auto operator <=> (fixed_point<I, F, S> x1, fixed_point<I, F, S> x2)
|
||||||
{
|
{
|
||||||
return x1.rep() <=> x2.rep();
|
return x1.rep() <=> x2.rep();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> abs(fixed_point<I, F, S> x)
|
constexpr fixed_point<I, F, S> abs(fixed_point<I, F, S> x)
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
|
|
@ -325,11 +345,42 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> round(fixed_point<I, F, S> x)
|
constexpr fixed_point<I, F, S> floor(fixed_point<I, F, S> x)
|
||||||
{
|
{
|
||||||
using FP = fixed_point<I, F, S>;
|
using FP = fixed_point<I, F, S>;
|
||||||
using rep_type = typename FP::rep_type;
|
using rep_type = typename FP::rep_type;
|
||||||
return FP::from_rep(x.rep() & ~((static_cast<rep_type>(1) << F) - 1));
|
constexpr auto fixed_point_one = static_cast<rep_type>(1) << F;
|
||||||
|
constexpr auto fractional_mask = fixed_point_one - static_cast<rep_type>(1);
|
||||||
|
return FP::from_rep(x.rep() & ~fractional_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
|
constexpr fixed_point<I, F, S> ceil(fixed_point<I, F, S> x)
|
||||||
|
{
|
||||||
|
using FP = fixed_point<I, F, S>;
|
||||||
|
using rep_type = typename FP::rep_type;
|
||||||
|
constexpr auto fixed_point_one = static_cast<rep_type>(1) << F;
|
||||||
|
constexpr auto fractional_mask = fixed_point_one - static_cast<rep_type>(1);
|
||||||
|
return FP::from_rep(((x.rep() - static_cast<rep_type>(1)) & ~fractional_mask) + fixed_point_one);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
|
constexpr fixed_point<I, F, S> round(fixed_point<I, F, S> x)
|
||||||
|
{
|
||||||
|
if constexpr (F == 0)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using FP = fixed_point<I, F, S>;
|
||||||
|
using rep_type = typename FP::rep_type;
|
||||||
|
constexpr auto fixed_point_half = static_cast<rep_type>(1) << (F - 1);
|
||||||
|
if ((x.rep() & fixed_point_half) == 0)
|
||||||
|
return floor(x);
|
||||||
|
else
|
||||||
|
return ceil(x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
|
|
@ -342,7 +393,7 @@ namespace psemek::util
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int I, unsigned int F, bool S>
|
template <unsigned int I, unsigned int F, bool S>
|
||||||
fixed_point<I, F, S> pow(fixed_point<I, F, S> x, int p)
|
constexpr fixed_point<I, F, S> pow(fixed_point<I, F, S> x, int p)
|
||||||
{
|
{
|
||||||
if (p < 0)
|
if (p < 0)
|
||||||
return fixed_point<I, F, S>(1) / pow(x, -p);
|
return fixed_point<I, F, S>(1) / pow(x, -p);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue