1 //===-- Utility class to manipulate fixed point numbers. --*- C++ -*-=========// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H 10 #define LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H 11 12 #include "include/llvm-libc-macros/stdfix-macros.h" 13 #include "src/__support/CPP/bit.h" 14 #include "src/__support/CPP/type_traits.h" 15 #include "src/__support/macros/attributes.h" // LIBC_INLINE 16 #include "src/__support/macros/config.h" 17 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY 18 #include "src/__support/math_extras.h" 19 20 #include "fx_rep.h" 21 22 #ifdef LIBC_COMPILER_HAS_FIXED_POINT 23 24 namespace LIBC_NAMESPACE_DECL { 25 namespace fixed_point { 26 27 template <typename T> struct FXBits { 28 private: 29 using fx_rep = FXRep<T>; 30 using StorageType = typename fx_rep::StorageType; 31 32 StorageType value; 33 34 static_assert(fx_rep::FRACTION_LEN > 0); 35 36 static constexpr size_t FRACTION_OFFSET = 0; // Just for completeness 37 static constexpr size_t INTEGRAL_OFFSET = 38 fx_rep::INTEGRAL_LEN == 0 ? 0 : fx_rep::FRACTION_LEN; 39 static constexpr size_t SIGN_OFFSET = 40 fx_rep::SIGN_LEN == 0 41 ? 0 42 : ((sizeof(StorageType) * CHAR_BIT) - fx_rep::SIGN_LEN); 43 44 static constexpr StorageType FRACTION_MASK = 45 mask_trailing_ones<StorageType, fx_rep::FRACTION_LEN>() 46 << FRACTION_OFFSET; 47 static constexpr StorageType INTEGRAL_MASK = 48 mask_trailing_ones<StorageType, fx_rep::INTEGRAL_LEN>() 49 << INTEGRAL_OFFSET; 50 static constexpr StorageType SIGN_MASK = 51 (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET); 52 53 public: 54 LIBC_INLINE constexpr FXBits() = default; 55 56 template <typename XType> LIBC_INLINE constexpr explicit FXBits(XType x) { 57 using Unqual = typename cpp::remove_cv_t<XType>; 58 if constexpr (cpp::is_same_v<Unqual, T>) { 59 value = cpp::bit_cast<StorageType>(x); 60 } else if constexpr (cpp::is_same_v<Unqual, StorageType>) { 61 value = x; 62 } else { 63 // We don't want accidental type promotions/conversions, so we require 64 // exact type match. 65 static_assert(cpp::always_false<XType>); 66 } 67 } 68 69 LIBC_INLINE constexpr StorageType get_fraction() { 70 return (value & FRACTION_MASK) >> FRACTION_OFFSET; 71 } 72 73 LIBC_INLINE constexpr StorageType get_integral() { 74 return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET; 75 } 76 77 // TODO: replace bool with Sign 78 LIBC_INLINE constexpr bool get_sign() { 79 return static_cast<bool>((value & SIGN_MASK) >> SIGN_OFFSET); 80 } 81 82 // This represents the effective negative exponent applied to this number 83 LIBC_INLINE constexpr int get_exponent() { return fx_rep::FRACTION_LEN; } 84 85 LIBC_INLINE constexpr void set_fraction(StorageType fraction) { 86 value = (value & (~FRACTION_MASK)) | 87 ((fraction << FRACTION_OFFSET) & FRACTION_MASK); 88 } 89 90 LIBC_INLINE constexpr void set_integral(StorageType integral) { 91 value = (value & (~INTEGRAL_MASK)) | 92 ((integral << INTEGRAL_OFFSET) & INTEGRAL_MASK); 93 } 94 95 // TODO: replace bool with Sign 96 LIBC_INLINE constexpr void set_sign(bool sign) { 97 value = (value & (~SIGN_MASK)) | 98 ((static_cast<StorageType>(sign) << SIGN_OFFSET) & SIGN_MASK); 99 } 100 101 LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(value); } 102 }; 103 104 // Bit-wise operations are not available for fixed point types yet. 105 template <typename T> 106 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> 107 bit_and(T x, T y) { 108 using BitType = typename FXRep<T>::StorageType; 109 BitType x_bit = cpp::bit_cast<BitType>(x); 110 BitType y_bit = cpp::bit_cast<BitType>(y); 111 // For some reason, bit_cast cannot deduce BitType from the input. 112 return cpp::bit_cast<T, BitType>(x_bit & y_bit); 113 } 114 115 template <typename T> 116 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> 117 bit_or(T x, T y) { 118 using BitType = typename FXRep<T>::StorageType; 119 BitType x_bit = cpp::bit_cast<BitType>(x); 120 BitType y_bit = cpp::bit_cast<BitType>(y); 121 // For some reason, bit_cast cannot deduce BitType from the input. 122 return cpp::bit_cast<T, BitType>(x_bit | y_bit); 123 } 124 125 template <typename T> 126 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T> 127 bit_not(T x) { 128 using BitType = typename FXRep<T>::StorageType; 129 BitType x_bit = cpp::bit_cast<BitType>(x); 130 // For some reason, bit_cast cannot deduce BitType from the input. 131 return cpp::bit_cast<T, BitType>(static_cast<BitType>(~x_bit)); 132 } 133 134 template <typename T> LIBC_INLINE constexpr T abs(T x) { 135 using FXRep = FXRep<T>; 136 if constexpr (FXRep::SIGN_LEN == 0) 137 return x; 138 else { 139 if (LIBC_UNLIKELY(x == FXRep::MIN())) 140 return FXRep::MAX(); 141 return (x < FXRep::ZERO() ? -x : x); 142 } 143 } 144 145 // Round-to-nearest, tie-to-(+Inf) 146 template <typename T> LIBC_INLINE constexpr T round(T x, int n) { 147 using FXRep = FXRep<T>; 148 if (LIBC_UNLIKELY(n < 0)) 149 n = 0; 150 if (LIBC_UNLIKELY(n >= FXRep::FRACTION_LEN)) 151 return x; 152 153 T round_bit = FXRep::EPS() << (FXRep::FRACTION_LEN - n - 1); 154 // Check for overflow. 155 if (LIBC_UNLIKELY(FXRep::MAX() - round_bit < x)) 156 return FXRep::MAX(); 157 158 T all_ones = bit_not(FXRep::ZERO()); 159 160 int shift = FXRep::FRACTION_LEN - n; 161 T rounding_mask = 162 (shift == FXRep::TOTAL_LEN) ? FXRep::ZERO() : (all_ones << shift); 163 return bit_and((x + round_bit), rounding_mask); 164 } 165 166 } // namespace fixed_point 167 } // namespace LIBC_NAMESPACE_DECL 168 169 #endif // LIBC_COMPILER_HAS_FIXED_POINT 170 171 #endif // LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H 172