| //===-- Utility class to manipulate fixed point numbers. --*- C++ -*-=========// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H |
| #define LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H |
| |
| #include "include/llvm-libc-macros/stdfix-macros.h" |
| #include "src/__support/CPP/algorithm.h" |
| #include "src/__support/CPP/bit.h" |
| #include "src/__support/CPP/limits.h" // numeric_limits |
| #include "src/__support/CPP/type_traits.h" |
| #include "src/__support/libc_assert.h" |
| #include "src/__support/macros/attributes.h" // LIBC_INLINE |
| #include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL |
| #include "src/__support/macros/null_check.h" // LIBC_CRASH_ON_VALUE |
| #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY |
| #include "src/__support/math_extras.h" |
| #include "src/__support/uint128.h" |
| |
| #include "fx_rep.h" |
| |
| #ifdef LIBC_COMPILER_HAS_FIXED_POINT |
| |
| namespace LIBC_NAMESPACE_DECL { |
| namespace fixed_point { |
| |
| template <typename T> struct FXBits { |
| private: |
| using fx_rep = FXRep<T>; |
| using StorageType = typename fx_rep::StorageType; |
| |
| StorageType value; |
| |
| static_assert(fx_rep::FRACTION_LEN > 0); |
| |
| static constexpr size_t FRACTION_OFFSET = 0; // Just for completeness |
| static constexpr size_t INTEGRAL_OFFSET = |
| fx_rep::INTEGRAL_LEN == 0 ? 0 : fx_rep::FRACTION_LEN; |
| static constexpr size_t SIGN_OFFSET = |
| fx_rep::SIGN_LEN == 0 |
| ? 0 |
| : ((sizeof(StorageType) * CHAR_BIT) - fx_rep::SIGN_LEN); |
| |
| static constexpr StorageType FRACTION_MASK = |
| mask_trailing_ones<StorageType, fx_rep::FRACTION_LEN>() |
| << FRACTION_OFFSET; |
| static constexpr StorageType INTEGRAL_MASK = |
| mask_trailing_ones<StorageType, fx_rep::INTEGRAL_LEN>() |
| << INTEGRAL_OFFSET; |
| static constexpr StorageType SIGN_MASK = |
| (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET); |
| |
| // mask for <integral | fraction> |
| static constexpr StorageType VALUE_MASK = INTEGRAL_MASK | FRACTION_MASK; |
| |
| // mask for <sign | integral | fraction> |
| static constexpr StorageType TOTAL_MASK = SIGN_MASK | VALUE_MASK; |
| |
| public: |
| LIBC_INLINE constexpr FXBits() = default; |
| |
| template <typename XType> LIBC_INLINE constexpr explicit FXBits(XType x) { |
| using Unqual = typename cpp::remove_cv_t<XType>; |
| if constexpr (cpp::is_same_v<Unqual, T>) { |
| value = cpp::bit_cast<StorageType>(x); |
| } else if constexpr (cpp::is_same_v<Unqual, StorageType>) { |
| value = x; |
| } else { |
| // We don't want accidental type promotions/conversions, so we require |
| // exact type match. |
| static_assert(cpp::always_false<XType>); |
| } |
| } |
| |
| LIBC_INLINE constexpr StorageType get_fraction() { |
| return (value & FRACTION_MASK) >> FRACTION_OFFSET; |
| } |
| |
| LIBC_INLINE constexpr StorageType get_integral() { |
| return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET; |
| } |
| |
| // returns complete bitstring representation the fixed point number |
| // the bitstring is of the form: padding | sign | integral | fraction |
| LIBC_INLINE constexpr StorageType get_bits() { |
| return (value & TOTAL_MASK) >> FRACTION_OFFSET; |
| } |
| |
| // TODO: replace bool with Sign |
| LIBC_INLINE constexpr bool get_sign() { |
| return static_cast<bool>((value & SIGN_MASK) >> SIGN_OFFSET); |
| } |
| |
| // This represents the effective negative exponent applied to this number |
| LIBC_INLINE constexpr int get_exponent() { return fx_rep::FRACTION_LEN; } |
| |
| LIBC_INLINE constexpr void set_fraction(StorageType fraction) { |
| value = (value & (~FRACTION_MASK)) | |
| ((fraction << FRACTION_OFFSET) & FRACTION_MASK); |
| } |
| |
| LIBC_INLINE constexpr void set_integral(StorageType integral) { |
| value = (value & (~INTEGRAL_MASK)) | |
| ((integral << INTEGRAL_OFFSET) & INTEGRAL_MASK); |
| } |
| |
| // TODO: replace bool with Sign |
| LIBC_INLINE constexpr void set_sign(bool sign) { |
| value = (value & (~SIGN_MASK)) | |
| ((static_cast<StorageType>(sign) << SIGN_OFFSET) & SIGN_MASK); |
| } |
| |
| LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(value); } |
| }; |
| |
| // Bit-wise operations are not available for fixed point types yet. |
| template <typename T> |
| LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> |
| bit_and(T x, T y) { |
| using BitType = typename FXRep<T>::StorageType; |
| BitType x_bit = cpp::bit_cast<BitType>(x); |
| BitType y_bit = cpp::bit_cast<BitType>(y); |
| // For some reason, bit_cast cannot deduce BitType from the input. |
| return cpp::bit_cast<T, BitType>(x_bit & y_bit); |
| } |
| |
| template <typename T> |
| LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> |
| bit_or(T x, T y) { |
| using BitType = typename FXRep<T>::StorageType; |
| BitType x_bit = cpp::bit_cast<BitType>(x); |
| BitType y_bit = cpp::bit_cast<BitType>(y); |
| // For some reason, bit_cast cannot deduce BitType from the input. |
| return cpp::bit_cast<T, BitType>(x_bit | y_bit); |
| } |
| |
| template <typename T> |
| LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> |
| bit_not(T x) { |
| using BitType = typename FXRep<T>::StorageType; |
| BitType x_bit = cpp::bit_cast<BitType>(x); |
| // For some reason, bit_cast cannot deduce BitType from the input. |
| return cpp::bit_cast<T, BitType>(static_cast<BitType>(~x_bit)); |
| } |
| |
| template <typename T> LIBC_INLINE constexpr T abs(T x) { |
| using FXRep = FXRep<T>; |
| if constexpr (FXRep::SIGN_LEN == 0) |
| return x; |
| else { |
| if (LIBC_UNLIKELY(x == FXRep::MIN())) |
| return FXRep::MAX(); |
| return (x < FXRep::ZERO() ? -x : x); |
| } |
| } |
| |
| // Round-to-nearest, tie-to-(+Inf) |
| template <typename T> LIBC_INLINE constexpr T round(T x, int n) { |
| using FXRep = FXRep<T>; |
| if (LIBC_UNLIKELY(n < 0)) |
| n = 0; |
| if (LIBC_UNLIKELY(n >= FXRep::FRACTION_LEN)) |
| return x; |
| |
| T round_bit = FXRep::EPS() << (FXRep::FRACTION_LEN - n - 1); |
| // Check for overflow. |
| if (LIBC_UNLIKELY(FXRep::MAX() - round_bit < x)) |
| return FXRep::MAX(); |
| |
| T all_ones = bit_not(FXRep::ZERO()); |
| |
| int shift = FXRep::FRACTION_LEN - n; |
| T rounding_mask = |
| (shift == FXRep::TOTAL_LEN) ? FXRep::ZERO() : (all_ones << shift); |
| return bit_and((x + round_bit), rounding_mask); |
| } |
| |
| // count leading sign bits |
| // TODO: support fixed_point_padding |
| template <typename T> |
| LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, int> |
| countls(T f) { |
| using FXRep = FXRep<T>; |
| using BitType = typename FXRep::StorageType; |
| using FXBits = FXBits<T>; |
| |
| if constexpr (FXRep::SIGN_LEN > 0) { |
| if (f < 0) |
| f = bit_not(f); |
| } |
| |
| BitType value_bits = FXBits(f).get_bits(); |
| return cpp::countl_zero(value_bits) - FXRep::SIGN_LEN; |
| } |
| |
| // fixed-point to integer conversion |
| template <typename T, typename XType> |
| LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, XType> |
| bitsfx(T f) { |
| return cpp::bit_cast<XType, T>(f); |
| } |
| |
| // divide the two fixed-point types and return an integer result |
| template <typename T, typename XType> |
| LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, XType> |
| idiv(T x, T y) { |
| using FXBits = FXBits<T>; |
| using FXRep = FXRep<T>; |
| using CompType = typename FXRep::CompType; |
| |
| // If the value of the second operand of the / operator is zero, the |
| // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16 |
| LIBC_CRASH_ON_VALUE(y, FXRep::ZERO()); |
| |
| CompType x_comp = static_cast<CompType>(FXBits(x).get_bits()); |
| CompType y_comp = static_cast<CompType>(FXBits(y).get_bits()); |
| |
| // If an integer result of one of these functions overflows, the behavior is |
| // undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16 |
| CompType result = x_comp / y_comp; |
| |
| return static_cast<XType>(result); |
| } |
| |
| // Divide two integers and return a fixed-point value. |
| // For reference, see: |
| // https://en.wikipedia.org/wiki/Division_algorithm#Newton%E2%80%93Raphson_division |
| // https://stackoverflow.com/a/9231996. |
| template <typename FXType, typename IntType> |
| LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<FXType>, FXType> |
| fxdivi(IntType n, IntType d) { |
| using OutRep = FXRep<FXType>; |
| static_assert(cpp::is_signed_v<IntType> == (OutRep::SIGN_LEN > 0), |
| "IntType and FXType must have matching signedness"); |
| constexpr bool IS_SIGNED = OutRep::SIGN_LEN > 0; |
| using UIntType = cpp::make_unsigned_t<IntType>; |
| |
| // If the value of the second operand of the / operator is zero, the |
| // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16. |
| LIBC_CRASH_ON_VALUE(d, 0); |
| if (LIBC_UNLIKELY(n == 0)) |
| return OutRep::ZERO(); |
| |
| // n == d and d != 0 means the quotient is 1. The general NR path can't |
| // guarantee landing on 1 exactly so special case for this. |
| if (LIBC_UNLIKELY(n == d)) { |
| if constexpr (OutRep::INTEGRAL_LEN > 0) |
| return static_cast<FXType>(1); |
| else |
| return OutRep::MAX(); |
| } |
| |
| // Intermediate arithmetic is done in a wide fixed-point type. |
| using WideFXType = |
| cpp::conditional_t<IS_SIGNED, long accum, unsigned long accum>; |
| using WideRep = FXRep<WideFXType>; |
| using WideStorage = typename WideRep::StorageType; |
| constexpr int F = OutRep::FRACTION_LEN; |
| constexpr int WF = WideRep::FRACTION_LEN; |
| |
| // Split each operand into a sign and a magnitude. |
| bool result_is_negative = false; |
| UIntType n_mag, d_mag; |
| if constexpr (IS_SIGNED) { |
| result_is_negative = (n < 0) != (d < 0); |
| n_mag = (n < 0) ? -static_cast<UIntType>(n) : static_cast<UIntType>(n); |
| d_mag = (d < 0) ? -static_cast<UIntType>(d) : static_cast<UIntType>(d); |
| } else { |
| n_mag = n; |
| d_mag = d; |
| } |
| |
| if constexpr (OutRep::INTEGRAL_LEN > 0) { |
| constexpr UInt128 MAX_UNITS = static_cast<UInt128>(1) |
| << OutRep::INTEGRAL_LEN; |
| |
| if (LIBC_UNLIKELY(static_cast<UInt128>(n_mag) >= |
| MAX_UNITS * static_cast<UInt128>(d_mag))) |
| return result_is_negative ? OutRep::MIN() : OutRep::MAX(); |
| } |
| |
| WideFXType res; |
| |
| constexpr int INTERMEDIATE_BITS = cpp::numeric_limits<UIntType>::digits + WF; |
| using WideIntType = |
| cpp::conditional_t<(INTERMEDIATE_BITS <= 64), uint64_t, UInt128>; |
| |
| if ((d_mag & (d_mag - 1)) == 0) { |
| // d is a power of 2. n/d is an exact right shift. |
| int log2_d = cpp::countr_zero(d_mag); |
| |
| WideIntType scaled_n = (static_cast<WideIntType>(n_mag) << WF) >> log2_d; |
| |
| constexpr int WIDE_STORAGE_BITS = cpp::numeric_limits<WideStorage>::digits; |
| if (LIBC_UNLIKELY((static_cast<UInt128>(scaled_n) >> WIDE_STORAGE_BITS) != |
| 0)) |
| return result_is_negative ? OutRep::MIN() : OutRep::MAX(); |
| |
| res = FXBits<WideFXType>(static_cast<WideStorage>(scaled_n)).get_val(); |
| } else if constexpr (WF <= F) { |
| WideIntType wide_n = static_cast<WideIntType>(n_mag) << WF; |
| WideIntType quotient = wide_n / static_cast<WideIntType>(d_mag); |
| res = FXBits<WideFXType>(static_cast<WideStorage>(quotient)).get_val(); |
| } else { |
| // General case: Approximate 1/d, then multiply by n. |
| |
| // Normalize d_mag into a WF fraction value in [0.5, 1) and apply the same |
| // shift to n_mag so that n_scaled/d_scaled = n/d. |
| constexpr int W = cpp::numeric_limits<UIntType>::digits; |
| int d_msb = (W - 1) - cpp::countl_zero(d_mag); |
| int norm_shift = (WF - 1) - d_msb; |
| |
| auto scale = [norm_shift](UIntType v) -> WideFXType { |
| WideStorage wide_v = static_cast<WideStorage>(v); |
| WideStorage shifted = |
| norm_shift >= 0 ? (wide_v << norm_shift) : (wide_v >> -norm_shift); |
| return FXBits<WideFXType>(shifted).get_val(); |
| }; |
| |
| WideFXType d_scaled = scale(d_mag); |
| WideFXType n_scaled = scale(n_mag); |
| |
| // Initial approximation of 1/d_scaled: x0 = 48/17 - (32/17) * d_scaled. |
| // d_scaled is in [0.5, 1) so x0 is in [0.941, 1.882] with a worst-case |
| // relative error bounded by 1/17 (~5.88%). |
| WideFXType a = static_cast<WideFXType>(0x2.d89d89d8p0lk); // 48/17 |
| WideFXType b = static_cast<WideFXType>(0x1.e1e1e1e1p0lk); // 32/17 |
| WideFXType initial_approx = a - b * d_scaled; |
| |
| auto nrstep = [](WideFXType d_, WideFXType x0) { |
| return x0 * (static_cast<WideFXType>(2) - d_ * x0); |
| }; |
| |
| // Each iteration squares the relative error (quadratic convergence). |
| WideFXType recip = nrstep(d_scaled, initial_approx); // E1 <= 0.346% |
| |
| if constexpr (F >= 7) |
| recip = nrstep(d_scaled, recip); // E2 <= 1.197e-5 |
| |
| if constexpr (F >= 15) |
| recip = nrstep(d_scaled, recip); // E3 <= 1.434e-10 |
| |
| res = n_scaled * recip; |
| } |
| |
| if constexpr (IS_SIGNED) { |
| if (result_is_negative) |
| res = -res; |
| } |
| |
| // According to clause 7.18a.6.1, saturate the result on overflow. |
| WideFXType max_val = static_cast<WideFXType>(OutRep::MAX()); |
| if (res > max_val) |
| return OutRep::MAX(); |
| if constexpr (IS_SIGNED) { |
| WideFXType min_val = static_cast<WideFXType>(OutRep::MIN()); |
| if (res < min_val) |
| return OutRep::MIN(); |
| } |
| |
| return static_cast<FXType>(res); |
| } |
| |
| // Divide an integer operand by a fixed-point operand and return the |
| // mathematically exact result as an IntType rounded towards 0. Assumes |
| // signedness of IntType matches the signedness of FXType. |
| template <typename IntType, typename FXType> |
| LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<FXType>, IntType> |
| divifx(IntType n, FXType d) { |
| using FXBits = FXBits<FXType>; |
| using FXRep = FXRep<FXType>; |
| using CompType = typename FXRep::CompType; |
| |
| static_assert(cpp::is_signed_v<IntType> == (FXRep::SIGN_LEN > 0), |
| "IntType and FXType must have matching signedness"); |
| |
| // UB if denominator is 0. |
| LIBC_CRASH_ON_VALUE(d, FXRep::ZERO()); |
| |
| if (LIBC_UNLIKELY(n == 0)) { |
| return static_cast<IntType>(0); |
| } |
| |
| CompType d_comp = static_cast<CompType>(FXBits(d).get_bits()); |
| |
| constexpr int F = FXRep::FRACTION_LEN; |
| |
| constexpr int INTERMEDIATE_BITS = |
| sizeof(IntType) * 8 - cpp::is_signed_v<IntType> + F; |
| |
| using WideType = cpp::conditional_t< |
| cpp::is_signed_v<IntType>, |
| cpp::conditional_t<INTERMEDIATE_BITS <= 64, int64_t, Int128>, |
| cpp::conditional_t<INTERMEDIATE_BITS <= 64, uint64_t, UInt128>>; |
| |
| WideType scaled_n = static_cast<WideType>(n) << F; |
| WideType result = scaled_n / static_cast<WideType>(d_comp); |
| |
| return static_cast<IntType>(result); |
| } |
| |
| } // namespace fixed_point |
| } // namespace LIBC_NAMESPACE_DECL |
| |
| #endif // LIBC_COMPILER_HAS_FIXED_POINT |
| |
| #endif // LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H |