//===-- 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/bit.h" #include "src/__support/CPP/type_traits.h" #include "src/__support/macros/attributes.h" // LIBC_INLINE #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY #include "src/__support/math_extras.h" #include "fx_rep.h" #ifdef LIBC_COMPILER_HAS_FIXED_POINT namespace LIBC_NAMESPACE_DECL { namespace fixed_point { template struct FXBits { private: using fx_rep = FXRep; 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() << FRACTION_OFFSET; static constexpr StorageType INTEGRAL_MASK = mask_trailing_ones() << INTEGRAL_OFFSET; static constexpr StorageType SIGN_MASK = (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET); public: LIBC_INLINE constexpr FXBits() = default; template LIBC_INLINE constexpr explicit FXBits(XType x) { using Unqual = typename cpp::remove_cv_t; if constexpr (cpp::is_same_v) { value = cpp::bit_cast(x); } else if constexpr (cpp::is_same_v) { value = x; } else { // We don't want accidental type promotions/conversions, so we require // exact type match. static_assert(cpp::always_false); } } LIBC_INLINE constexpr StorageType get_fraction() { return (value & FRACTION_MASK) >> FRACTION_OFFSET; } LIBC_INLINE constexpr StorageType get_integral() { return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET; } // TODO: replace bool with Sign LIBC_INLINE constexpr bool get_sign() { return static_cast((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(sign) << SIGN_OFFSET) & SIGN_MASK); } LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast(value); } }; // Bit-wise operations are not available for fixed point types yet. template LIBC_INLINE constexpr cpp::enable_if_t, T> bit_and(T x, T y) { using BitType = typename FXRep::StorageType; BitType x_bit = cpp::bit_cast(x); BitType y_bit = cpp::bit_cast(y); // For some reason, bit_cast cannot deduce BitType from the input. return cpp::bit_cast(x_bit & y_bit); } template LIBC_INLINE constexpr cpp::enable_if_t, T> bit_or(T x, T y) { using BitType = typename FXRep::StorageType; BitType x_bit = cpp::bit_cast(x); BitType y_bit = cpp::bit_cast(y); // For some reason, bit_cast cannot deduce BitType from the input. return cpp::bit_cast(x_bit | y_bit); } template LIBC_INLINE constexpr cpp::enable_if_t, T> bit_not(T x) { using BitType = typename FXRep::StorageType; BitType x_bit = cpp::bit_cast(x); // For some reason, bit_cast cannot deduce BitType from the input. return cpp::bit_cast(static_cast(~x_bit)); } template LIBC_INLINE constexpr T abs(T x) { using FXRep = FXRep; 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 LIBC_INLINE constexpr T round(T x, int n) { using FXRep = FXRep; 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); } } // namespace fixed_point } // namespace LIBC_NAMESPACE_DECL #endif // LIBC_COMPILER_HAS_FIXED_POINT #endif // LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H