1708 lines
50 KiB
C++
1708 lines
50 KiB
C++
|
|
/// @file grotto/fixedpoint.hpp
|
||
|
|
/// @brief
|
||
|
|
/// @details
|
||
|
|
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||
|
|
/// @copyright Copyright (c) 2019-2023 Ryan Henry and others
|
||
|
|
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
|
||
|
|
/// see [LICENSE.md](@ref GPLv2) for details.
|
||
|
|
|
||
|
|
#ifndef LIBDPF_INCLUDE_DPF_FIXEDPOINT_HPP__
|
||
|
|
#define LIBDPF_INCLUDE_DPF_FIXEDPOINT_HPP__
|
||
|
|
|
||
|
|
#include "hedley/hedley.h"
|
||
|
|
#include <portable-snippets/exact-int/exact-int.h>
|
||
|
|
#include <portable-snippets/builtin/builtin.h>
|
||
|
|
|
||
|
|
#include <cstddef>
|
||
|
|
#include <cstdint>
|
||
|
|
#include <cinttypes>
|
||
|
|
#include <limits>
|
||
|
|
#include <type_traits>
|
||
|
|
#include <functional>
|
||
|
|
#include <array>
|
||
|
|
#include <stdexcept>
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include <cmath>
|
||
|
|
#include <iostream>
|
||
|
|
|
||
|
|
#include "dpf/utils.hpp"
|
||
|
|
#include "dpf/leaf_arithmetic.hpp"
|
||
|
|
#include "dpf/uint256_t.hpp"
|
||
|
|
|
||
|
|
#define GROTTO_FIXED_DEFAULT_INTEGRAL_REPRESENTATION psnip_uint64_t
|
||
|
|
|
||
|
|
namespace grotto
|
||
|
|
{
|
||
|
|
|
||
|
|
namespace detail
|
||
|
|
{
|
||
|
|
|
||
|
|
/// @brief Integer value of an already-rounded finite double, as a 256-bit word.
|
||
|
|
/// Values that do not fit saturate to all-ones.
|
||
|
|
inline uint256_t uint256_from_rounded_double(double rounded) noexcept
|
||
|
|
{
|
||
|
|
if (!(rounded > 0.0) || !std::isfinite(rounded))
|
||
|
|
{
|
||
|
|
return uint256_t{0};
|
||
|
|
}
|
||
|
|
int exp = 0;
|
||
|
|
const double frac = std::frexp(rounded, &exp);
|
||
|
|
if (exp <= 0)
|
||
|
|
{
|
||
|
|
return uint256_t{0};
|
||
|
|
}
|
||
|
|
if (exp > 256)
|
||
|
|
{
|
||
|
|
return ~uint256_t{0};
|
||
|
|
}
|
||
|
|
constexpr int mant_bits = 53;
|
||
|
|
const auto mant = static_cast<std::uint64_t>(std::ldexp(frac, mant_bits));
|
||
|
|
const int place = exp - mant_bits;
|
||
|
|
simde_uint128 chunk = mant;
|
||
|
|
int bit = place;
|
||
|
|
if (bit < 0)
|
||
|
|
{
|
||
|
|
chunk >>= static_cast<unsigned>(-bit);
|
||
|
|
bit = 0;
|
||
|
|
}
|
||
|
|
uint128_t lower{0};
|
||
|
|
uint128_t upper{0};
|
||
|
|
if (bit < 128)
|
||
|
|
{
|
||
|
|
const simde_uint128 lowbits = chunk << static_cast<unsigned>(bit);
|
||
|
|
lower = uint128_t{static_cast<std::uint64_t>(lowbits >> 64),
|
||
|
|
static_cast<std::uint64_t>(lowbits)};
|
||
|
|
if (bit > 128 - mant_bits)
|
||
|
|
{
|
||
|
|
const simde_uint128 hibits = chunk >> static_cast<unsigned>(128 - bit);
|
||
|
|
upper = uint128_t{static_cast<std::uint64_t>(hibits >> 64),
|
||
|
|
static_cast<std::uint64_t>(hibits)};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
const simde_uint128 hibits = chunk << static_cast<unsigned>(bit - 128);
|
||
|
|
upper = uint128_t{static_cast<std::uint64_t>(hibits >> 64),
|
||
|
|
static_cast<std::uint64_t>(hibits)};
|
||
|
|
}
|
||
|
|
return uint256_t{upper, lower};
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename To, typename From, typename = void>
|
||
|
|
struct is_static_castable : std::false_type {};
|
||
|
|
|
||
|
|
template <typename To, typename From>
|
||
|
|
struct is_static_castable<To, From,
|
||
|
|
std::void_t<decltype(static_cast<To>(std::declval<From>()))>>
|
||
|
|
: std::true_type {};
|
||
|
|
|
||
|
|
/// Low `bits` of `wide`, saturated to all-ones when `wide` does not fit.
|
||
|
|
template <typename Raw, std::size_t Bits>
|
||
|
|
Raw saturate_low_bits(uint256_t wide) noexcept
|
||
|
|
{
|
||
|
|
static_assert(Bits > 0 && Bits <= 256);
|
||
|
|
if constexpr (Bits <= 64)
|
||
|
|
{
|
||
|
|
const std::uint64_t low = wide.lower().lower();
|
||
|
|
const bool overflow = wide.upper() != uint128_t{0}
|
||
|
|
|| wide.lower().upper() != 0
|
||
|
|
|| (Bits < 64 && (low >> Bits) != 0);
|
||
|
|
const std::uint64_t mag = overflow
|
||
|
|
? (Bits == 64 ? ~std::uint64_t{0} : ((std::uint64_t{1} << Bits) - 1))
|
||
|
|
: low;
|
||
|
|
return static_cast<Raw>(mag);
|
||
|
|
}
|
||
|
|
else if constexpr (Bits <= 128)
|
||
|
|
{
|
||
|
|
const std::uint64_t lo = wide.lower().lower();
|
||
|
|
const std::uint64_t hi = wide.lower().upper();
|
||
|
|
const bool overflow = wide.upper() != uint128_t{0}
|
||
|
|
|| (Bits < 128 && (hi >> (Bits - 64)) != 0);
|
||
|
|
simde_uint128 mag;
|
||
|
|
if (overflow)
|
||
|
|
{
|
||
|
|
if constexpr (Bits == 128)
|
||
|
|
mag = ~simde_uint128{0};
|
||
|
|
else
|
||
|
|
mag = (simde_uint128{1} << Bits) - 1;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
mag = (static_cast<simde_uint128>(hi) << 64) | lo;
|
||
|
|
}
|
||
|
|
return static_cast<Raw>(mag);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
uint256_t mag = wide;
|
||
|
|
if constexpr (Bits < 256)
|
||
|
|
{
|
||
|
|
const auto sh = static_cast<unsigned>(Bits - 128);
|
||
|
|
const bool overflow = (wide.upper() >> sh) != uint128_t{0};
|
||
|
|
if (overflow)
|
||
|
|
{
|
||
|
|
const uint128_t hi = (uint128_t{1} << sh) - 1;
|
||
|
|
mag = uint256_t{hi, ~uint128_t{0}};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return static_cast<Raw>(mag);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename IntegralType>
|
||
|
|
inline IntegralType rounded_double_to_integral(double rounded) noexcept
|
||
|
|
{
|
||
|
|
if constexpr (std::is_integral_v<IntegralType>
|
||
|
|
|| std::is_same_v<IntegralType, simde_int128>
|
||
|
|
|| std::is_same_v<IntegralType, simde_uint128>)
|
||
|
|
{
|
||
|
|
return static_cast<IntegralType>(rounded);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
const bool neg = std::signbit(rounded);
|
||
|
|
const double mag = neg ? -rounded : rounded;
|
||
|
|
const uint256_t wide = uint256_from_rounded_double(mag);
|
||
|
|
IntegralType result{};
|
||
|
|
if constexpr (std::is_same_v<IntegralType, uint128_t>)
|
||
|
|
{
|
||
|
|
result = wide.upper() != uint128_t{0} ? ~uint128_t{0} : wide.lower();
|
||
|
|
}
|
||
|
|
else if constexpr (std::is_same_v<IntegralType, uint256_t>)
|
||
|
|
{
|
||
|
|
result = wide;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
constexpr std::size_t bits = dpf::utils::bitlength_of_v<IntegralType>;
|
||
|
|
using raw_type = typename dpf::utils::make_from_integral_value<IntegralType>::integral_type;
|
||
|
|
if constexpr (bits > 0 && bits <= 256
|
||
|
|
&& is_static_castable<IntegralType, raw_type>::value)
|
||
|
|
{
|
||
|
|
const raw_type raw = saturate_low_bits<raw_type, bits>(wide);
|
||
|
|
result = dpf::utils::make_from_integral_value<IntegralType>{}(raw);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
static_assert(bits > 0 && bits <= 256
|
||
|
|
&& is_static_castable<IntegralType, raw_type>::value,
|
||
|
|
"fixedpoint construction from double is not implemented for this integer type");
|
||
|
|
return IntegralType{};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (neg)
|
||
|
|
{
|
||
|
|
result = -result;
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Shift an integer into fixed-point raw form: `value * 2^FractionalBits`,
|
||
|
|
/// wrapping in the backend's two's-complement encoding. One shift; no `double`.
|
||
|
|
template <typename IntegralType,
|
||
|
|
unsigned FractionalBits,
|
||
|
|
typename T>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr IntegralType scale_integer_to_fixed_raw(T integer_value) noexcept
|
||
|
|
{
|
||
|
|
using unsigned_type = dpf::utils::make_unsigned_t<IntegralType>;
|
||
|
|
const auto bits = static_cast<unsigned_type>(
|
||
|
|
static_cast<IntegralType>(integer_value));
|
||
|
|
if constexpr (FractionalBits == 0)
|
||
|
|
{
|
||
|
|
return static_cast<IntegralType>(bits);
|
||
|
|
}
|
||
|
|
return static_cast<IntegralType>(bits << FractionalBits);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename IntegralType>
|
||
|
|
inline constexpr bool is_signed_rep_v =
|
||
|
|
std::is_signed_v<IntegralType>
|
||
|
|
|| std::is_same_v<IntegralType, simde_int128>;
|
||
|
|
|
||
|
|
/// Two's-complement negate via the unsigned width. Defined for the
|
||
|
|
/// most-negative value (wraps); signed `-x` would be UB there.
|
||
|
|
template <typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr IntegralType raw_neg(IntegralType x) noexcept
|
||
|
|
{
|
||
|
|
using unsigned_type = dpf::utils::make_unsigned_t<IntegralType>;
|
||
|
|
return static_cast<IntegralType>(
|
||
|
|
unsigned_type{} - static_cast<unsigned_type>(x));
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr IntegralType raw_abs(IntegralType x) noexcept
|
||
|
|
{
|
||
|
|
if constexpr (is_signed_rep_v<IntegralType>)
|
||
|
|
{
|
||
|
|
return (x < IntegralType{}) ? raw_neg(x) : x;
|
||
|
|
}
|
||
|
|
return x;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Remainder with the sign of `a` and magnitude `< |b|` (C++ `%` /
|
||
|
|
/// `std::fmod`). Zero divisor → 0; this type has no NaN.
|
||
|
|
template <typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr IntegralType raw_fmod(IntegralType a, IntegralType b) noexcept
|
||
|
|
{
|
||
|
|
if (b == IntegralType{})
|
||
|
|
{
|
||
|
|
return IntegralType{};
|
||
|
|
}
|
||
|
|
return a % b;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace detail
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept;
|
||
|
|
|
||
|
|
/// @tparam FractionalBits Number of fractional bits used in the fixed-point
|
||
|
|
/// representation.
|
||
|
|
/// @tparam IntegralType The underlying integral type used for the fixed-point
|
||
|
|
/// representation.
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType = GROTTO_FIXED_DEFAULT_INTEGRAL_REPRESENTATION>
|
||
|
|
struct fixedpoint
|
||
|
|
{
|
||
|
|
using integral_type = IntegralType;
|
||
|
|
static constexpr int fractional_bits = FractionalBits;
|
||
|
|
static constexpr int integer_bits = dpf::utils::bitlength_of_v<integral_type> - fractional_bits;
|
||
|
|
|
||
|
|
static_assert(std::numeric_limits<integral_type>::is_integer
|
||
|
|
|| std::is_same_v<integral_type, simde_int128>
|
||
|
|
|| std::is_same_v<integral_type, simde_uint128>);
|
||
|
|
static_assert(fractional_bits <= dpf::utils::bitlength_of_v<integral_type>);
|
||
|
|
static_assert(integer_bits >= 0);
|
||
|
|
|
||
|
|
private:
|
||
|
|
struct raw_tag {};
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
/// @name C'tors
|
||
|
|
/// @brief Constructs a new fixed-point number.
|
||
|
|
/// @{
|
||
|
|
|
||
|
|
/// @brief Default c'tor
|
||
|
|
/// @details Zero-initializes the encoding (`fixedpoint x;` is 0).
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint() noexcept = default;
|
||
|
|
|
||
|
|
/// @brief Copy c'tor
|
||
|
|
/// @details Constructs a fixed-point with the value copied from `other`.
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint(const fixedpoint & other) noexcept = default;
|
||
|
|
|
||
|
|
/// @brief Move c'tor
|
||
|
|
/// @details Constructs a fixed-point with the value copied from `other` using move semantics.
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint(fixedpoint && other) noexcept = default;
|
||
|
|
|
||
|
|
/// @brief Value c'tor
|
||
|
|
/// @details Initializes the fixed-point with the value determined by `desired`, using the <a href="https://en.cppreference.com/w/cpp/numeric/fenv/FE_round">current rounding mode</a> for the least-significant bit.
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint(double desired) noexcept // NOLINT (implicit c'tor)
|
||
|
|
: value{detail::rounded_double_to_integral<integral_type>(
|
||
|
|
std::nearbyint(std::ldexp(desired, fractional_bits)))}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
/// @brief Integer value c'tor
|
||
|
|
/// @details `fixedpoint(3)` is the mathematical value 3 (raw encoding
|
||
|
|
/// `3 << fractional_bits`), not a raw word. One shift; no `double`.
|
||
|
|
/// Use `from_raw` for a bit-exact encoding.
|
||
|
|
template <typename T,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_integral_v<T>
|
||
|
|
&& !std::is_same_v<std::remove_cv_t<T>, bool>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint(T integer_value) noexcept // NOLINT (implicit c'tor)
|
||
|
|
: value{detail::scale_integer_to_fixed_raw<
|
||
|
|
integral_type, static_cast<unsigned>(fractional_bits)>(integer_value)}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
/// @brief Bit-exact construction from the backend integer encoding.
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
static constexpr fixedpoint from_raw(integral_type raw) noexcept
|
||
|
|
{
|
||
|
|
return fixedpoint{raw, raw_tag{}};
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @}
|
||
|
|
|
||
|
|
/// @name Assignment operators
|
||
|
|
/// @brief Assign a new value to a fixed-point number
|
||
|
|
/// {@
|
||
|
|
|
||
|
|
/// @brief Copy assignment
|
||
|
|
/// @details Assigns the fixed-point with a copy of `other`
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator=(const fixedpoint & other) noexcept = default;
|
||
|
|
|
||
|
|
/// @brief Move assignment
|
||
|
|
/// @details Assigns the fixed-point with a copy of `other` using move semantics.
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator=(fixedpoint && other) noexcept = default;
|
||
|
|
|
||
|
|
/// @brief Value assignment
|
||
|
|
/// @details Assigns the fixed-point with a value determined by `desired`, using the <a href="https://en.cppreference.com/w/cpp/numeric/fenv/FE_round">current rounding mode</a> for the least-significant bit..
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator=(const double & desired) noexcept
|
||
|
|
{
|
||
|
|
value = detail::rounded_double_to_integral<integral_type>(
|
||
|
|
std::nearbyint(std::ldexp(desired, fractional_bits)));
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @}
|
||
|
|
|
||
|
|
~fixedpoint() = default;
|
||
|
|
|
||
|
|
/// @brief Cast to `double`
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_PURE
|
||
|
|
explicit constexpr operator double() const noexcept
|
||
|
|
{
|
||
|
|
return std::ldexp(static_cast<double>(value), -static_cast<double>(fractional_bits));
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator&(integral_type mask) const noexcept
|
||
|
|
{
|
||
|
|
return static_cast<bool>(this->integral_representation() & mask);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Bit test against another encoding (DPF writes `mask & x` with both
|
||
|
|
/// sides the input type when `msb_mask` is a `fixedpoint`).
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator&(fixedpoint mask) const noexcept
|
||
|
|
{
|
||
|
|
return static_cast<bool>(value & mask.value);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Bitwise complement of the encoding. `std::bit_not` uses this.
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr fixedpoint operator~() const noexcept
|
||
|
|
{
|
||
|
|
using unsigned_type = dpf::utils::make_unsigned_t<integral_type>;
|
||
|
|
return from_raw(static_cast<integral_type>(
|
||
|
|
~static_cast<unsigned_type>(value)));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Next / previous representable encoding (one ULP).
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator++() noexcept
|
||
|
|
{
|
||
|
|
++value;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint operator++(int) noexcept
|
||
|
|
{
|
||
|
|
fixedpoint tmp = *this;
|
||
|
|
++*this;
|
||
|
|
return tmp;
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator--() noexcept
|
||
|
|
{
|
||
|
|
--value;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint operator--(int) noexcept
|
||
|
|
{
|
||
|
|
fixedpoint tmp = *this;
|
||
|
|
--*this;
|
||
|
|
return tmp;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Logical shift of the encoding. DPF walks `msb_mask` with `>>`; a
|
||
|
|
/// signed arithmetic shift would sign-extend the MSB and break that.
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator>>=(std::size_t n) noexcept
|
||
|
|
{
|
||
|
|
using unsigned_type = dpf::utils::make_unsigned_t<integral_type>;
|
||
|
|
value = static_cast<integral_type>(static_cast<unsigned_type>(value) >> n);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator<<=(std::size_t n) noexcept
|
||
|
|
{
|
||
|
|
using unsigned_type = dpf::utils::make_unsigned_t<integral_type>;
|
||
|
|
value = static_cast<integral_type>(static_cast<unsigned_type>(value) << n);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
friend constexpr fixedpoint operator>>(fixedpoint x, std::size_t n) noexcept
|
||
|
|
{
|
||
|
|
x >>= n;
|
||
|
|
return x;
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
friend constexpr fixedpoint operator<<(fixedpoint x, std::size_t n) noexcept
|
||
|
|
{
|
||
|
|
x <<= n;
|
||
|
|
return x;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Access underlying integral representation
|
||
|
|
/// @details If the represented fixed-point number is `x`, then this
|
||
|
|
/// function returns an `integral_type` whose value is `x*2**fractional_bits`.
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr integral_type integral_representation() const noexcept
|
||
|
|
{
|
||
|
|
return this->value;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Unary negation operator
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr fixedpoint operator-() const noexcept
|
||
|
|
{
|
||
|
|
return from_raw(detail::raw_neg(value));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Binary addition operator
|
||
|
|
/// @details Computes the sum of two fixed-point numbers
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr fixedpoint operator+(fixedpoint rhs) const noexcept
|
||
|
|
{
|
||
|
|
return from_raw(value + rhs.value);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Binary addition assignment operator
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator+=(fixedpoint rhs) noexcept
|
||
|
|
{
|
||
|
|
this->value += rhs.integral_representation();
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Binary subtraction operator
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr fixedpoint operator-(fixedpoint rhs) const noexcept
|
||
|
|
{
|
||
|
|
return from_raw(value - rhs.value);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Binary addition assignment operator
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint & operator-=(fixedpoint rhs) noexcept
|
||
|
|
{
|
||
|
|
this->value -= rhs.integral_representation();
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Binary multiplication operator
|
||
|
|
template <unsigned FractionalBits1>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr auto operator*(fixedpoint<FractionalBits1, IntegralType> rhs) const noexcept
|
||
|
|
{
|
||
|
|
return make_fixed_from_integral_type<FractionalBits + FractionalBits1>((this->integral_representation() * rhs.integral_representation()));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @name Equality
|
||
|
|
/// @brief Strict equality operator
|
||
|
|
/// @{
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator==(fixedpoint rhs) const noexcept
|
||
|
|
{
|
||
|
|
return (this->integral_representation() == rhs.integral_representation());
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator==(double rhs) const noexcept
|
||
|
|
{
|
||
|
|
return is_in_range(rhs) && (*this == fixedpoint(rhs));
|
||
|
|
}
|
||
|
|
/// @}
|
||
|
|
|
||
|
|
/// @name Inequality
|
||
|
|
/// @brief Strict inequality operator
|
||
|
|
/// @{
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator!=(fixedpoint rhs) const noexcept
|
||
|
|
{
|
||
|
|
return (this->integral_representation() != rhs.integral_representation());
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator!=(double rhs) const noexcept
|
||
|
|
{
|
||
|
|
return !(*this == rhs);
|
||
|
|
}
|
||
|
|
/// @}
|
||
|
|
|
||
|
|
/// @name Less than
|
||
|
|
/// @brief Binary less-than operator
|
||
|
|
/// @{
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator<(fixedpoint rhs) const noexcept
|
||
|
|
{
|
||
|
|
return (this->integral_representation() < rhs.integral_representation());
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator<(double rhs) const noexcept
|
||
|
|
{
|
||
|
|
if (std::isnan(rhs))
|
||
|
|
return false;
|
||
|
|
if (!is_in_range(rhs))
|
||
|
|
return rhs > static_cast<double>(std::numeric_limits<fixedpoint>::max());
|
||
|
|
return *this < fixedpoint(rhs);
|
||
|
|
}
|
||
|
|
/// @}
|
||
|
|
|
||
|
|
/// @name Less than or equal
|
||
|
|
/// @brief Binary less-than-or-equal operator
|
||
|
|
/// @{
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator<=(fixedpoint rhs) const noexcept
|
||
|
|
{
|
||
|
|
return (this->integral_representation() <= rhs.integral_representation());
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator<=(double rhs) const noexcept
|
||
|
|
{
|
||
|
|
if (std::isnan(rhs))
|
||
|
|
return false;
|
||
|
|
if (!is_in_range(rhs))
|
||
|
|
return rhs > static_cast<double>(std::numeric_limits<fixedpoint>::max());
|
||
|
|
return *this <= fixedpoint(rhs);
|
||
|
|
}
|
||
|
|
/// @}
|
||
|
|
|
||
|
|
/// @name Greater than
|
||
|
|
/// @brief Binary greater-than operator
|
||
|
|
/// @{
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator>(fixedpoint rhs) const noexcept
|
||
|
|
{
|
||
|
|
return (this->integral_representation() > rhs.integral_representation());
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator>(double rhs) const noexcept
|
||
|
|
{
|
||
|
|
if (std::isnan(rhs))
|
||
|
|
return false;
|
||
|
|
if (!is_in_range(rhs))
|
||
|
|
return rhs < static_cast<double>(std::numeric_limits<fixedpoint>::lowest());
|
||
|
|
return *this > fixedpoint(rhs);
|
||
|
|
}
|
||
|
|
/// @}
|
||
|
|
|
||
|
|
/// @name Greater than or equal
|
||
|
|
/// @brief Binary greater-than-or-equal operator
|
||
|
|
/// @{
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator>=(fixedpoint rhs) const noexcept
|
||
|
|
{
|
||
|
|
return (this->integral_representation() >= rhs.integral_representation());
|
||
|
|
}
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_PURE
|
||
|
|
constexpr bool operator>=(double rhs) const noexcept
|
||
|
|
{
|
||
|
|
if (std::isnan(rhs))
|
||
|
|
return false;
|
||
|
|
if (!is_in_range(rhs))
|
||
|
|
return rhs < static_cast<double>(std::numeric_limits<fixedpoint>::lowest());
|
||
|
|
return *this >= fixedpoint(rhs);
|
||
|
|
}
|
||
|
|
/// @}
|
||
|
|
|
||
|
|
private:
|
||
|
|
// struct make_fixed_from_integral_type_tag {};
|
||
|
|
|
||
|
|
/// @brief Determine if a floating-point is within range
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
static constexpr bool is_in_range(double d) noexcept
|
||
|
|
{
|
||
|
|
return HEDLEY_LIKELY(
|
||
|
|
static_cast<double>(std::numeric_limits<fixedpoint>::lowest()) <= d
|
||
|
|
&& d <= static_cast<double>(std::numeric_limits<fixedpoint>::max()));
|
||
|
|
}
|
||
|
|
|
||
|
|
// HEDLEY_ALWAYS_INLINE
|
||
|
|
// HEDLEY_NO_THROW
|
||
|
|
// constexpr explicit fixedpoint(integral_type val, make_fixed_from_integral_type_tag &&) noexcept
|
||
|
|
// : value{val}
|
||
|
|
// { }
|
||
|
|
|
||
|
|
// template <unsigned F, typename T> friend constexpr auto nextafter(fixedpoint<F, T>) noexcept;
|
||
|
|
// template <unsigned F, typename T> friend constexpr auto nextbefore(fixedpoint<F, T>) noexcept;
|
||
|
|
// template <unsigned F0, unsigned F1, typename T> friend constexpr auto precision_cast(fixedpoint<F1, T>) noexcept;
|
||
|
|
template <unsigned F, typename T> friend auto constexpr make_fixed_from_integral_type(T) noexcept;
|
||
|
|
// template <unsigned F, typename T> friend constexpr auto fabs(fixedpoint<F, T>) noexcept;
|
||
|
|
// template <unsigned F, typename T> friend constexpr auto fmod(fixedpoint<F, T>, double) noexcept;
|
||
|
|
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
constexpr fixedpoint(integral_type raw, raw_tag) noexcept
|
||
|
|
: value{raw}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
integral_type value{};
|
||
|
|
};
|
||
|
|
|
||
|
|
/// @brief Bit test with the mask on the left. DPF key generation and
|
||
|
|
/// evaluation write `mask & x`.
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType,
|
||
|
|
typename Mask>
|
||
|
|
constexpr bool operator&(const Mask & mask,
|
||
|
|
const fixedpoint<FractionalBits, IntegralType> & x) noexcept
|
||
|
|
{
|
||
|
|
return static_cast<bool>(x.integral_representation()
|
||
|
|
& static_cast<IntegralType>(mask));
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class CharT,
|
||
|
|
class Traits,
|
||
|
|
unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
std::basic_ostream<CharT, Traits> &
|
||
|
|
operator<<(std::basic_ostream<CharT, Traits> & os,
|
||
|
|
const fixedpoint<FractionalBits, IntegralType> & f) noexcept
|
||
|
|
{
|
||
|
|
return os << static_cast<double>(f);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class CharT,
|
||
|
|
class Traits,
|
||
|
|
unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
std::basic_istream<CharT, Traits> &
|
||
|
|
operator>>(std::basic_istream<CharT, Traits> & is,
|
||
|
|
fixedpoint<FractionalBits, IntegralType> & f)
|
||
|
|
{
|
||
|
|
double d;
|
||
|
|
is >> d;
|
||
|
|
f = d;
|
||
|
|
return is;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept
|
||
|
|
{
|
||
|
|
return fixedpoint<FractionalBits, IntegralType>::from_raw(value);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType = GROTTO_FIXED_DEFAULT_INTEGRAL_REPRESENTATION>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
static constexpr auto make_fixed(double d)
|
||
|
|
{
|
||
|
|
return fixedpoint<FractionalBits, IntegralType>(d);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename FixedType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
static constexpr auto make_fixed(double d)
|
||
|
|
{
|
||
|
|
return fixedpoint<FixedType::fractional_bits, typename FixedType::integral_type>(d);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// @brief Creates a fixed-point number from a double with bounds checking.
|
||
|
|
/// @throws std::range_error If the input double is outside the representable
|
||
|
|
/// range of the fixed-point number.
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType = GROTTO_FIXED_DEFAULT_INTEGRAL_REPRESENTATION>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_CONST
|
||
|
|
static auto make_fixed_safe(double d)
|
||
|
|
{
|
||
|
|
using fixed_type = fixedpoint<FractionalBits, IntegralType>;
|
||
|
|
|
||
|
|
if (HEDLEY_UNLIKELY(d < std::numeric_limits<fixed_type>::lowest()))
|
||
|
|
{
|
||
|
|
throw std::range_error("value is too small (underflows integral representation)");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (HEDLEY_UNLIKELY(std::numeric_limits<fixed_type>::max() < d))
|
||
|
|
{
|
||
|
|
throw std::range_error("value is too large (overflows integral representation)");
|
||
|
|
}
|
||
|
|
|
||
|
|
return make_fixed<FractionalBits, IntegralType>(d);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned ToFractionalBits,
|
||
|
|
unsigned FromFractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
constexpr auto precision_cast(const fixedpoint<FromFractionalBits, IntegralType> & f) noexcept
|
||
|
|
{
|
||
|
|
auto value = f.integral_representation();
|
||
|
|
if constexpr (ToFractionalBits > FromFractionalBits)
|
||
|
|
{
|
||
|
|
return make_fixed_from_integral_type<ToFractionalBits, IntegralType>(value << (ToFractionalBits - FromFractionalBits));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return make_fixed_from_integral_type<ToFractionalBits, IntegralType>(value >> (FromFractionalBits - ToFractionalBits));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
static constexpr auto precision_of(fixedpoint<FractionalBits, IntegralType>) noexcept
|
||
|
|
{
|
||
|
|
return FractionalBits;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr auto nextafter(fixedpoint<FractionalBits, IntegralType> f) noexcept
|
||
|
|
{
|
||
|
|
return make_fixed_from_integral_type<FractionalBits, IntegralType>(f.integral_representation()+1);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr auto nextbefore(fixedpoint<FractionalBits, IntegralType> f) noexcept
|
||
|
|
{
|
||
|
|
return make_fixed_from_integral_type<FractionalBits, IntegralType>(f.integral_representation()-1);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr auto fabs(fixedpoint<FractionalBits, IntegralType> v) noexcept
|
||
|
|
{
|
||
|
|
if constexpr (!detail::is_signed_rep_v<IntegralType>)
|
||
|
|
{
|
||
|
|
return v;
|
||
|
|
}
|
||
|
|
const auto mag = v.integral_representation();
|
||
|
|
if (mag < IntegralType{})
|
||
|
|
{
|
||
|
|
return fixedpoint<FractionalBits, IntegralType>::from_raw(
|
||
|
|
detail::raw_neg(mag));
|
||
|
|
}
|
||
|
|
return v;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr auto fmod(fixedpoint<FractionalBits, IntegralType> v,
|
||
|
|
fixedpoint<FractionalBits, IntegralType> modulus) noexcept
|
||
|
|
{
|
||
|
|
return fixedpoint<FractionalBits, IntegralType>::from_raw(
|
||
|
|
detail::raw_fmod(v.integral_representation(),
|
||
|
|
modulus.integral_representation()));
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr auto fmod(fixedpoint<FractionalBits, IntegralType> v,
|
||
|
|
double modulus) noexcept
|
||
|
|
{
|
||
|
|
return fmod(v, make_fixed<FractionalBits, IntegralType>(modulus));
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType,
|
||
|
|
typename T,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_integral_v<T>
|
||
|
|
&& !std::is_same_v<std::remove_cv_t<T>, bool>, int> = 0>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr auto fmod(fixedpoint<FractionalBits, IntegralType> v,
|
||
|
|
T modulus) noexcept
|
||
|
|
{
|
||
|
|
return fmod(v, fixedpoint<FractionalBits, IntegralType>(modulus));
|
||
|
|
}
|
||
|
|
|
||
|
|
enum fixed_cast_policy
|
||
|
|
{
|
||
|
|
use_default,
|
||
|
|
use_left_arg,
|
||
|
|
use_right_arg,
|
||
|
|
use_min_arg,
|
||
|
|
use_max_arg,
|
||
|
|
use_arg_sum //< for multiplies only
|
||
|
|
};
|
||
|
|
|
||
|
|
template <typename BinaryOperator,
|
||
|
|
fixed_cast_policy Mode = use_max_arg>
|
||
|
|
struct binary_operator_precast_wrapper
|
||
|
|
{
|
||
|
|
static_assert(Mode == use_default ||
|
||
|
|
Mode == use_left_arg ||
|
||
|
|
Mode == use_right_arg ||
|
||
|
|
Mode == use_min_arg ||
|
||
|
|
Mode == use_max_arg);
|
||
|
|
|
||
|
|
template <typename IntegralType,
|
||
|
|
unsigned FractionalBitsLHS,
|
||
|
|
unsigned FractionalBitsRHS>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr auto operator()(
|
||
|
|
fixedpoint<FractionalBitsLHS, IntegralType> lhs,
|
||
|
|
fixedpoint<FractionalBitsRHS, IntegralType> rhs) const noexcept
|
||
|
|
{
|
||
|
|
constexpr bool lt = (FractionalBitsLHS < FractionalBitsRHS);
|
||
|
|
constexpr bool eq = (FractionalBitsLHS == FractionalBitsRHS);
|
||
|
|
constexpr BinaryOperator op{};
|
||
|
|
|
||
|
|
if constexpr (eq) return op(lhs, rhs); // no cast necessary
|
||
|
|
else if constexpr ((Mode == use_left_arg) // always casting to lhs
|
||
|
|
|| ((Mode == use_min_arg) && lt) // lhs happens to be min
|
||
|
|
|| ((Mode == use_max_arg) && !lt) // lhs happens to be max
|
||
|
|
|| ((Mode == use_default) && !lt)) // default == use_max
|
||
|
|
{
|
||
|
|
return op(lhs, precision_cast<FractionalBitsLHS>(rhs));
|
||
|
|
}
|
||
|
|
else if constexpr ((Mode == use_right_arg) // always casting to rhs
|
||
|
|
|| ((Mode == use_min_arg) && !lt) // rhs happens to be min
|
||
|
|
|| ((Mode == use_max_arg) && lt) // rhs happens to be max
|
||
|
|
|| ((Mode == use_default) && lt)) // default == use_max
|
||
|
|
{
|
||
|
|
return op(precision_cast<FractionalBitsRHS>(lhs), rhs);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
HEDLEY_UNREACHABLE();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <fixed_cast_policy Mode = use_arg_sum>
|
||
|
|
struct multiplies
|
||
|
|
{
|
||
|
|
static_assert(Mode == use_default ||
|
||
|
|
Mode == use_left_arg ||
|
||
|
|
Mode == use_right_arg ||
|
||
|
|
Mode == use_min_arg ||
|
||
|
|
Mode == use_max_arg ||
|
||
|
|
Mode == use_arg_sum);
|
||
|
|
|
||
|
|
template <unsigned FractionalBitsOut = unsigned(-1),
|
||
|
|
typename IntegralType,
|
||
|
|
unsigned FractionalBitsLHS,
|
||
|
|
unsigned FractionalBitsRHS>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr auto operator()(
|
||
|
|
fixedpoint<FractionalBitsLHS, IntegralType> lhs,
|
||
|
|
fixedpoint<FractionalBitsRHS, IntegralType> rhs) const noexcept
|
||
|
|
{
|
||
|
|
// First element is the right-shift that brings the raw product down to
|
||
|
|
// `FractionalBitsOut`. It is negative when the product must be shifted left.
|
||
|
|
constexpr auto r = []() -> std::pair<int, unsigned>
|
||
|
|
{
|
||
|
|
constexpr bool lt = (FractionalBitsLHS < FractionalBitsRHS);
|
||
|
|
constexpr bool eq = (FractionalBitsLHS == FractionalBitsRHS);
|
||
|
|
if constexpr (FractionalBitsOut != unsigned(-1))
|
||
|
|
{
|
||
|
|
return {static_cast<int>(FractionalBitsLHS + FractionalBitsRHS)
|
||
|
|
- static_cast<int>(FractionalBitsOut),
|
||
|
|
FractionalBitsOut};
|
||
|
|
}
|
||
|
|
if constexpr (eq)
|
||
|
|
{
|
||
|
|
return {static_cast<int>((FractionalBitsLHS+FractionalBitsLHS)/2),
|
||
|
|
1u+(FractionalBitsLHS+FractionalBitsLHS-1)/2};
|
||
|
|
}
|
||
|
|
if constexpr ((Mode == use_left_arg)
|
||
|
|
|| ((Mode == use_min_arg) && lt)
|
||
|
|
|| ((Mode == use_max_arg) && !lt))
|
||
|
|
{
|
||
|
|
return {static_cast<int>(FractionalBitsRHS), FractionalBitsLHS};
|
||
|
|
}
|
||
|
|
if constexpr ((Mode == use_right_arg)
|
||
|
|
|| ((Mode == use_min_arg) && !lt)
|
||
|
|
|| ((Mode == use_max_arg) && lt))
|
||
|
|
{
|
||
|
|
return {static_cast<int>(FractionalBitsLHS), FractionalBitsRHS};
|
||
|
|
}
|
||
|
|
else if constexpr ((Mode == use_arg_sum) || (Mode == use_default))
|
||
|
|
{
|
||
|
|
return {0, FractionalBitsRHS+FractionalBitsLHS};
|
||
|
|
}
|
||
|
|
}();
|
||
|
|
|
||
|
|
if constexpr (std::numeric_limits<IntegralType>::digits > 32)
|
||
|
|
{
|
||
|
|
simde_uint128 product = simde_uint128(lhs.integral_representation())
|
||
|
|
* rhs.integral_representation();
|
||
|
|
if constexpr (r.first < 0)
|
||
|
|
product = product << -r.first;
|
||
|
|
else
|
||
|
|
product = product >> r.first;
|
||
|
|
return make_fixed_from_integral_type<r.second, IntegralType>(
|
||
|
|
static_cast<IntegralType>(product));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
std::uint64_t product = std::uint64_t(lhs.integral_representation())
|
||
|
|
* rhs.integral_representation();
|
||
|
|
if constexpr (r.first < 0)
|
||
|
|
product = product << -r.first;
|
||
|
|
else
|
||
|
|
product = product >> static_cast<unsigned>(r.first);
|
||
|
|
return make_fixed_from_integral_type<r.second, IntegralType>(
|
||
|
|
static_cast<IntegralType>(product));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr bool operator==(double lhs, fixedpoint<FractionalBits, IntegralType> rhs) noexcept
|
||
|
|
{
|
||
|
|
return (rhs == lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr bool operator<(double lhs, fixedpoint<FractionalBits, IntegralType> rhs) noexcept
|
||
|
|
{
|
||
|
|
return (rhs > lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr bool operator<=(double lhs, fixedpoint<FractionalBits, IntegralType> rhs) noexcept
|
||
|
|
{
|
||
|
|
return (rhs >= lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr bool operator>(double lhs, fixedpoint<FractionalBits, IntegralType> rhs) noexcept
|
||
|
|
{
|
||
|
|
return (rhs < lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_CONST
|
||
|
|
constexpr bool operator>=(double lhs, fixedpoint<FractionalBits, IntegralType> rhs) noexcept
|
||
|
|
{
|
||
|
|
return (rhs <= lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename FixedPointType,
|
||
|
|
std::size_t Degree>
|
||
|
|
struct fixedpoint_polynomial : public std::array<FixedPointType, Degree>
|
||
|
|
{
|
||
|
|
using coefficient_type = FixedPointType;
|
||
|
|
static constexpr std::size_t degree = Degree;
|
||
|
|
constexpr auto operator()(coefficient_type x) const
|
||
|
|
{
|
||
|
|
constexpr auto product_of = multiplies<use_arg_sum>{};
|
||
|
|
constexpr auto sum_of = binary_operator_precast_wrapper<std::plus<coefficient_type>, use_max_arg>{};
|
||
|
|
auto coeff = this->rbegin();
|
||
|
|
coefficient_type y{*coeff};
|
||
|
|
while (++coeff != this->rend())
|
||
|
|
{
|
||
|
|
y = sum_of(product_of(y, x), *coeff);
|
||
|
|
}
|
||
|
|
return y;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <typename FixedPointType,
|
||
|
|
std::size_t Degree>
|
||
|
|
static constexpr auto evaluate(const fixedpoint_polynomial<FixedPointType, Degree> & poly, FixedPointType x)
|
||
|
|
{
|
||
|
|
return poly(x);
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace fixedpoint_literals
|
||
|
|
{
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed0(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<0>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed1(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<1>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed2(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<2>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed3(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<3>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed4(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<4>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed5(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<5>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed6(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<6>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed7(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<7>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed8(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<8>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed9(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<9>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed10(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<10>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed11(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<11>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed12(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<12>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed13(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<13>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed14(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<14>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed15(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<15>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed16(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<16>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed17(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<17>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed18(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<18>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed19(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<19>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed20(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<20>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed21(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<21>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed22(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<22>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed23(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<23>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed24(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<24>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed25(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<25>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed26(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<26>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed27(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<27>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed28(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<28>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed29(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<29>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed30(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<30>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed31(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<31>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed32(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<32>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr auto operator "" _fixed33(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<33>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed34(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<34>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed35(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<35>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed36(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<36>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed37(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<37>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed38(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<38>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed39(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<39>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed40(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<40>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed41(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<41>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed42(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<42>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed43(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<43>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed44(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<44>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed45(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<45>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed46(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<46>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed47(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<47>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed48(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<48>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed49(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<49>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed50(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<50>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed51(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<51>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed52(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<52>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed53(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<53>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed54(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<54>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed55(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<55>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed56(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<56>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed57(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<57>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed58(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<58>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed59(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<59>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed60(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<60>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed61(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<61>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed62(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<62>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed63(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<63>(val);
|
||
|
|
}
|
||
|
|
constexpr auto operator "" _fixed64(long double val)
|
||
|
|
{
|
||
|
|
return grotto::make_fixed<64>(val);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace grotto::fixedpoint_literals
|
||
|
|
|
||
|
|
} // namespace grotto
|
||
|
|
|
||
|
|
using grotto::precision_cast;
|
||
|
|
|
||
|
|
namespace dpf
|
||
|
|
{
|
||
|
|
|
||
|
|
namespace utils
|
||
|
|
{
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct bitlength_of<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
: public bitlength_of<IntegralType> { };
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct msb_of<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
{
|
||
|
|
static constexpr grotto::fixedpoint<FractionalBits, IntegralType> value =
|
||
|
|
grotto::fixedpoint<FractionalBits, IntegralType>::from_raw(
|
||
|
|
static_cast<IntegralType>(msb_of_v<IntegralType>));
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct uses_signed_msb<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
: std::bool_constant<uses_signed_msb_v<IntegralType>> { };
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct make_unsigned<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
{
|
||
|
|
using type = grotto::fixedpoint<FractionalBits, make_unsigned_t<IntegralType>>;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct countl_zero_symmetric_difference<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
{
|
||
|
|
using T = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||
|
|
static constexpr auto clz = dpf::utils::countl_zero_symmetric_difference<typename T::integral_type>{};
|
||
|
|
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
constexpr std::size_t operator()(const T & lhs, const T & rhs) const noexcept
|
||
|
|
{
|
||
|
|
return clz(lhs.integral_representation(), rhs.integral_representation());
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct to_integral_type<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
: to_integral_type_base<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
{
|
||
|
|
using parent = to_integral_type_base<grotto::fixedpoint<FractionalBits, IntegralType>>;
|
||
|
|
using typename parent::integral_type;
|
||
|
|
|
||
|
|
HEDLEY_PURE
|
||
|
|
HEDLEY_NO_THROW
|
||
|
|
HEDLEY_ALWAYS_INLINE
|
||
|
|
constexpr integral_type operator()(
|
||
|
|
const grotto::fixedpoint<FractionalBits, IntegralType> & input) const noexcept
|
||
|
|
{
|
||
|
|
return to_integral_type<IntegralType>{}(input.integral_representation());
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct mod_pow_2<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
{
|
||
|
|
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||
|
|
static constexpr auto mod = mod_pow_2<IntegralType>{};
|
||
|
|
|
||
|
|
std::size_t operator()(fixed_type val, std::size_t n) const noexcept
|
||
|
|
{
|
||
|
|
return mod(val.integral_representation(), n);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct make_from_integral_value<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
{
|
||
|
|
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||
|
|
using integral_type = typename to_integral_type<fixed_type>::integral_type;
|
||
|
|
|
||
|
|
constexpr fixed_type operator()(integral_type val) const noexcept
|
||
|
|
{
|
||
|
|
return grotto::make_fixed_from_integral_type<FractionalBits, IntegralType>(
|
||
|
|
static_cast<IntegralType>(val));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
struct flip_msb_for_input<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
{
|
||
|
|
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||
|
|
|
||
|
|
constexpr void operator()(fixed_type & x) const
|
||
|
|
{
|
||
|
|
if constexpr (uses_signed_msb_v<fixed_type>)
|
||
|
|
{
|
||
|
|
using unsigned_type = make_unsigned_t<IntegralType>;
|
||
|
|
auto raw = static_cast<unsigned_type>(x.integral_representation());
|
||
|
|
raw ^= static_cast<unsigned_type>(msb_of_v<IntegralType>);
|
||
|
|
x = grotto::make_fixed_from_integral_type<FractionalBits, IntegralType>(
|
||
|
|
static_cast<IntegralType>(raw));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace dpf::utils
|
||
|
|
|
||
|
|
} // namespace dpf
|
||
|
|
|
||
|
|
namespace dpf::leaf_arithmetic
|
||
|
|
{
|
||
|
|
|
||
|
|
template <unsigned FractionalBits, typename IntegralType>
|
||
|
|
struct add_t<grotto::fixedpoint<FractionalBits, IntegralType>, simde__m128i>
|
||
|
|
{
|
||
|
|
auto operator()(const simde__m128i & a, const simde__m128i & b) const
|
||
|
|
{
|
||
|
|
return add_t<IntegralType, simde__m128i>{}(a, b);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits, typename IntegralType>
|
||
|
|
struct add_t<grotto::fixedpoint<FractionalBits, IntegralType>, simde__m256i>
|
||
|
|
{
|
||
|
|
auto operator()(const simde__m256i & a, const simde__m256i & b) const
|
||
|
|
{
|
||
|
|
return add_t<IntegralType, simde__m256i>{}(a, b);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits, typename IntegralType>
|
||
|
|
struct subtract_t<grotto::fixedpoint<FractionalBits, IntegralType>, simde__m128i>
|
||
|
|
{
|
||
|
|
auto operator()(const simde__m128i & a, const simde__m128i & b) const
|
||
|
|
{
|
||
|
|
return subtract_t<IntegralType, simde__m128i>{}(a, b);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits, typename IntegralType>
|
||
|
|
struct subtract_t<grotto::fixedpoint<FractionalBits, IntegralType>, simde__m256i>
|
||
|
|
{
|
||
|
|
auto operator()(const simde__m256i & a, const simde__m256i & b) const
|
||
|
|
{
|
||
|
|
return subtract_t<IntegralType, simde__m256i>{}(a, b);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits, typename IntegralType>
|
||
|
|
struct multiply_t<grotto::fixedpoint<FractionalBits, IntegralType>, simde__m128i>
|
||
|
|
{
|
||
|
|
auto operator()(const simde__m128i & a,
|
||
|
|
grotto::fixedpoint<FractionalBits, IntegralType> b) const
|
||
|
|
{
|
||
|
|
return multiply_t<IntegralType, simde__m128i>{}(a, b.integral_representation());
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned FractionalBits, typename IntegralType>
|
||
|
|
struct multiply_t<grotto::fixedpoint<FractionalBits, IntegralType>, simde__m256i>
|
||
|
|
{
|
||
|
|
auto operator()(const simde__m256i & a,
|
||
|
|
grotto::fixedpoint<FractionalBits, IntegralType> b) const
|
||
|
|
{
|
||
|
|
return multiply_t<IntegralType, simde__m256i>{}(a, b.integral_representation());
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace dpf::leaf_arithmetic
|
||
|
|
|
||
|
|
namespace std
|
||
|
|
{
|
||
|
|
|
||
|
|
template <unsigned FractionalBits,
|
||
|
|
typename IntegralType>
|
||
|
|
class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
||
|
|
{
|
||
|
|
using T = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||
|
|
using I = IntegralType;
|
||
|
|
static constexpr bool signed_rep =
|
||
|
|
std::is_signed_v<I> || std::is_same_v<I, simde_int128>;
|
||
|
|
static constexpr int bitwidth =
|
||
|
|
static_cast<int>(dpf::utils::bitlength_of_v<I>);
|
||
|
|
|
||
|
|
static constexpr I raw_lowest() noexcept
|
||
|
|
{
|
||
|
|
if constexpr (signed_rep)
|
||
|
|
{
|
||
|
|
using U = dpf::utils::make_unsigned_t<I>;
|
||
|
|
return static_cast<I>(U{1} << static_cast<unsigned>(bitwidth - 1));
|
||
|
|
}
|
||
|
|
return I{};
|
||
|
|
}
|
||
|
|
|
||
|
|
static constexpr I raw_max() noexcept
|
||
|
|
{
|
||
|
|
if constexpr (signed_rep)
|
||
|
|
{
|
||
|
|
using U = dpf::utils::make_unsigned_t<I>;
|
||
|
|
return static_cast<I>(static_cast<U>(raw_lowest()) - U{1});
|
||
|
|
}
|
||
|
|
return static_cast<I>(~I{});
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
static constexpr bool is_specialized = true;
|
||
|
|
static constexpr bool is_signed = signed_rep;
|
||
|
|
static constexpr bool is_integer = (FractionalBits == 0);
|
||
|
|
static constexpr bool is_exact = true;
|
||
|
|
static constexpr bool has_infinity = false;
|
||
|
|
static constexpr bool has_quiet_NaN = false;
|
||
|
|
static constexpr bool has_signaling_NaN = false;
|
||
|
|
static constexpr float_denorm_style has_denorm = denorm_absent;
|
||
|
|
static constexpr bool has_denorm_loss = false;
|
||
|
|
static constexpr float_round_style round_style =
|
||
|
|
FractionalBits ? round_to_nearest : round_toward_zero;
|
||
|
|
static constexpr bool is_iec559 = false;
|
||
|
|
static constexpr bool is_bounded = true;
|
||
|
|
static constexpr bool is_modulo = !signed_rep;
|
||
|
|
static constexpr int digits = bitwidth - (signed_rep ? 1 : 0);
|
||
|
|
static constexpr int digits10 = digits * 301 / 1000;
|
||
|
|
static constexpr int max_digits10 = 0;
|
||
|
|
static constexpr int radix = 2;
|
||
|
|
static constexpr int min_exponent = 1 - static_cast<int>(FractionalBits);
|
||
|
|
static constexpr int max_exponent =
|
||
|
|
digits - static_cast<int>(FractionalBits) + 1;
|
||
|
|
static constexpr int min_exponent10 = min_exponent * 301 / 1000;
|
||
|
|
static constexpr int max_exponent10 = max_exponent * 301 / 1000;
|
||
|
|
static constexpr bool traps = false;
|
||
|
|
static constexpr bool tinyness_before = false;
|
||
|
|
|
||
|
|
static constexpr T lowest() noexcept { return T::from_raw(raw_lowest()); }
|
||
|
|
static constexpr T max() noexcept { return T::from_raw(raw_max()); }
|
||
|
|
static constexpr T min() noexcept
|
||
|
|
{
|
||
|
|
if constexpr (FractionalBits == 0)
|
||
|
|
{
|
||
|
|
return lowest();
|
||
|
|
}
|
||
|
|
return T::from_raw(I{1});
|
||
|
|
}
|
||
|
|
static constexpr T epsilon() noexcept
|
||
|
|
{
|
||
|
|
return FractionalBits ? T::from_raw(I{1}) : T::from_raw(I{});
|
||
|
|
}
|
||
|
|
static constexpr T round_error() noexcept
|
||
|
|
{
|
||
|
|
return FractionalBits ? T(0.5) : T(0);
|
||
|
|
}
|
||
|
|
static constexpr T infinity() noexcept { return max(); }
|
||
|
|
static constexpr T quiet_NaN() noexcept { return T::from_raw(I{}); }
|
||
|
|
static constexpr T signalling_NaN() noexcept { return T::from_raw(I{}); }
|
||
|
|
static constexpr T denorm_min() noexcept { return min(); }
|
||
|
|
};
|
||
|
|
|
||
|
|
template <unsigned F, typename T>
|
||
|
|
struct numeric_limits<grotto::fixedpoint<F, T> const>
|
||
|
|
: public numeric_limits<grotto::fixedpoint<F, T>> {};
|
||
|
|
|
||
|
|
template <unsigned F, typename T>
|
||
|
|
struct numeric_limits<grotto::fixedpoint<F, T> volatile>
|
||
|
|
: public numeric_limits<grotto::fixedpoint<F, T>> {};
|
||
|
|
|
||
|
|
template <unsigned F, typename T>
|
||
|
|
struct numeric_limits<grotto::fixedpoint<F, T> const volatile>
|
||
|
|
: public numeric_limits<grotto::fixedpoint<F, T>> {};
|
||
|
|
|
||
|
|
} // namespace std
|
||
|
|
|
||
|
|
#include "grotto/fixedpoint_mul.hpp"
|
||
|
|
|
||
|
|
#endif // LIBDPF_INCLUDE_GROTTO_FIXEDPOINT_HPP__
|