libdpf/include/grotto/fixedpoint.hpp

1800 lines
54 KiB
C++

/// @file grotto/fixedpoint.hpp
/// @brief Fixed-point values stored in an integer backend.
/// @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.
/// @param rounded the `rounded`
/// @return Integer value of an already-rounded finite double, as a 256-bit word
HEDLEY_NO_THROW
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 {};
/// @brief Low `bits` of `wide`, saturated to all-ones when `wide` does not fit.
/// @tparam Raw underlying representation
/// @tparam Bits bits
/// @param wide the `wide`
/// @return Low `bits` of `wide`, saturated to all-ones when `wide` does not fit
template <typename Raw, std::size_t Bits>
HEDLEY_NO_THROW
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>
HEDLEY_NO_THROW
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;
}
}
/// @brief Shift an integer into fixed-point raw form: `value * 2^FractionalBits`,
/// wrapping in the backend's two's-complement encoding. One shift; no `double`.
/// @tparam IntegralType underlying integral type
/// @tparam FractionalBits number of fractional bits
/// @tparam T value type
/// @param integer_value the `integer_value`
/// @return the returned `IntegralType`
template <typename IntegralType,
unsigned FractionalBits,
typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
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>;
/// @brief Two's-complement negate via the unsigned width. Defined for the
/// most-negative value (wraps); signed `-x` would be UB there.
/// @tparam IntegralType underlying integral type
/// @param x the `x`
/// @return Two's-complement negate via the unsigned width
template <typename IntegralType>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
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
HEDLEY_NO_THROW
constexpr IntegralType raw_abs(IntegralType x) noexcept
{
if constexpr (is_signed_rep_v<IntegralType>)
{
return (x < IntegralType{}) ? raw_neg(x) : x;
}
return x;
}
/// @brief Remainder with the sign of `a` and magnitude `< |b|` (C++ `%` /
/// `std::fmod`). Zero divisor → 0; this type has no NaN.
/// @tparam IntegralType underlying integral type
/// @param a the `a`
/// @param b the `b`
/// @return Remainder with the sign of `a` and magnitude `< |b|` (C++ `%` / `std::fmod`)
template <typename IntegralType>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr IntegralType raw_fmod(IntegralType a, IntegralType b) noexcept
{
if (b == IntegralType{})
{
return IntegralType{};
}
return a % b;
}
} // namespace detail
template <unsigned FractionalBits,
typename IntegralType>
HEDLEY_NO_THROW
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept;
/// @tparam FractionalBits Number of fractional bits used in the fixed-point
/// @brief 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`.
/// @param other the value to compare or copy
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.
/// @param other the value to compare or copy
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.
/// @param desired the `desired`
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.
/// @tparam T value type
/// @tparam T value type
/// @param integer_value the `integer_value`
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.
/// @param raw the underlying integer
/// @return 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`
/// @param other the value to compare or copy
/// @return `*this`
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.
/// @param other the value to compare or copy
/// @return `*this`
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..
/// @param desired the `desired`
/// @return `*this`
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`
/// @return Cast to `double`
HEDLEY_NO_THROW
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);
}
/// @brief Bit test against another encoding (DPF writes `mask & x` with both
/// sides the input type when `msb_mask` is a `fixedpoint`).
/// @param mask the bit mask
/// @return 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);
}
/// @brief Bitwise complement of the encoding. `std::bit_not` uses this.
/// @return Bitwise complement of the encoding
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)));
}
/// @brief Next / previous representable encoding (one ULP).
/// @return `*this`
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;
}
/// @brief 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`.
/// @return Access underlying integral representation
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
HEDLEY_PURE
constexpr integral_type integral_representation() const noexcept
{
return this->value;
}
/// @brief Unary negation operator
/// @return 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
/// @param rhs the right-hand operand
/// @return Binary addition 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
/// @param rhs the right-hand operand
/// @return `*this`
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr fixedpoint & operator+=(fixedpoint rhs) noexcept
{
this->value += rhs.integral_representation();
return *this;
}
/// @brief Binary subtraction operator
/// @param rhs the right-hand operand
/// @return 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
/// @param rhs the right-hand operand
/// @return `*this`
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr fixedpoint & operator-=(fixedpoint rhs) noexcept
{
this->value -= rhs.integral_representation();
return *this;
}
/// @brief Binary multiplication operator
/// @tparam FractionalBits1 fractional bits1
/// @param rhs the right-hand operand
/// @return 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
/// @param d the `d`
/// @return 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`.
/// @tparam FractionalBits number of fractional bits
/// @tparam IntegralType underlying integral type
/// @tparam Mask mask
/// @param mask the bit mask
/// @param x the `x`
/// @return Bit test with the mask on the left
template <unsigned FractionalBits,
typename IntegralType,
typename Mask>
HEDLEY_NO_THROW
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>
HEDLEY_NO_THROW
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>
HEDLEY_NO_THROW
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.
/// @tparam FractionalBits number of fractional bits
/// @tparam IntegralType underlying integral type
/// @param d the `d`
/// @return 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>
HEDLEY_NO_THROW
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>
HEDLEY_NO_THROW
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_NO_THROW
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>{};
HEDLEY_NO_THROW
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;
HEDLEY_NO_THROW
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
{
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
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());
}
};
HEDLEY_PRAGMA(GCC diagnostic pop)
} // 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>);
HEDLEY_NO_THROW
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{};
}
HEDLEY_NO_THROW
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;
HEDLEY_NO_THROW
static constexpr T lowest() noexcept { return T::from_raw(raw_lowest()); }
HEDLEY_NO_THROW
static constexpr T max() noexcept { return T::from_raw(raw_max()); }
HEDLEY_NO_THROW
static constexpr T min() noexcept
{
if constexpr (FractionalBits == 0)
{
return lowest();
}
return T::from_raw(I{1});
}
HEDLEY_NO_THROW
static constexpr T epsilon() noexcept
{
return FractionalBits ? T::from_raw(I{1}) : T::from_raw(I{});
}
HEDLEY_NO_THROW
static constexpr T round_error() noexcept
{
return FractionalBits ? T(0.5) : T(0);
}
HEDLEY_NO_THROW
static constexpr T infinity() noexcept { return max(); }
HEDLEY_NO_THROW
static constexpr T quiet_NaN() noexcept { return T::from_raw(I{}); }
HEDLEY_NO_THROW
static constexpr T signalling_NaN() noexcept { return T::from_raw(I{}); }
HEDLEY_NO_THROW
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__