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>
|
||||
H clamp(T x, H min, H max)
|
||||
constexpr H clamp(T x, H min, H max)
|
||||
{
|
||||
if (x < static_cast<T>(min))
|
||||
return min;
|
||||
|
|
@ -147,32 +147,37 @@ namespace psemek::util
|
|||
using unsigned_rep_type = typename traits::unsigned_rep_type;
|
||||
using unsigned_wide_type = typename traits::unsigned_wide_type;
|
||||
|
||||
fixed_point() = default;
|
||||
fixed_point(fixed_point const &) = default;
|
||||
constexpr fixed_point() = default;
|
||||
constexpr fixed_point(fixed_point const &) = default;
|
||||
|
||||
constexpr fixed_point & operator = (fixed_point const &) = default;
|
||||
|
||||
template <std::integral T>
|
||||
explicit fixed_point(T value);
|
||||
constexpr explicit fixed_point(T value);
|
||||
|
||||
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>
|
||||
explicit operator T () const;
|
||||
constexpr explicit operator T () const;
|
||||
|
||||
template <std::floating_point T>
|
||||
explicit operator T () const;
|
||||
constexpr explicit operator T () const;
|
||||
|
||||
fixed_point & operator += (fixed_point);
|
||||
fixed_point & operator -= (fixed_point);
|
||||
fixed_point & operator *= (fixed_point);
|
||||
fixed_point & operator /= (fixed_point);
|
||||
constexpr fixed_point & operator += (fixed_point);
|
||||
constexpr fixed_point & operator -= (fixed_point);
|
||||
constexpr 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 fixed_point max();
|
||||
static constexpr fixed_point min();
|
||||
static constexpr fixed_point max();
|
||||
|
||||
private:
|
||||
rep_type rep_;
|
||||
|
|
@ -180,32 +185,47 @@ namespace psemek::util
|
|||
|
||||
template <unsigned int I, unsigned int F, bool S>
|
||||
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)
|
||||
{}
|
||||
|
||||
template <unsigned int I, unsigned int F, bool S>
|
||||
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())))
|
||||
{}
|
||||
|
||||
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 <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;
|
||||
}
|
||||
|
||||
template <unsigned int I, unsigned int F, bool S>
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
result.rep_ = rep;
|
||||
|
|
@ -213,7 +233,7 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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 rep_type = typename FP::rep_type;
|
||||
|
|
@ -221,7 +241,7 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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 rep_type = typename FP::rep_type;
|
||||
|
|
@ -229,7 +249,7 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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 rep_type = typename FP::rep_type;
|
||||
|
|
@ -238,7 +258,7 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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 rep_type = typename FP::rep_type;
|
||||
|
|
@ -247,7 +267,7 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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 rep_type = typename FP::rep_type;
|
||||
|
|
@ -256,16 +276,16 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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 rep_type = typename FP::rep_type;
|
||||
using wide_type = typename FP::wide_type;
|
||||
return FP::from_rep(static_cast<rep_type>((static_cast<wide_type>(x1.rep()) * static_cast<wide_type>(x2.rep())) >> F));
|
||||
using unsigned_wide_type = typename FP::unsigned_wide_type;
|
||||
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>
|
||||
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 rep_type = typename FP::rep_type;
|
||||
|
|
@ -274,47 +294,47 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
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;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
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;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
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;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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 rep_type = typename FP::rep_type;
|
||||
|
|
@ -325,11 +345,42 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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 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>
|
||||
|
|
@ -342,7 +393,7 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
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)
|
||||
return fixed_point<I, F, S>(1) / pow(x, -p);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue