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