xref: /freebsd/contrib/llvm-project/libc/src/__support/fixed_point/fx_bits.h (revision bb722a7d0f1642bff6487f943ad0427799a6e5bf)
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/limits.h" // numeric_limits
15 #include "src/__support/CPP/type_traits.h"
16 #include "src/__support/macros/attributes.h"   // LIBC_INLINE
17 #include "src/__support/macros/config.h"       // LIBC_NAMESPACE_DECL
18 #include "src/__support/macros/null_check.h"   // LIBC_CRASH_ON_VALUE
19 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
20 #include "src/__support/math_extras.h"
21 
22 #include "fx_rep.h"
23 
24 #ifdef LIBC_COMPILER_HAS_FIXED_POINT
25 
26 namespace LIBC_NAMESPACE_DECL {
27 namespace fixed_point {
28 
29 template <typename T> struct FXBits {
30 private:
31   using fx_rep = FXRep<T>;
32   using StorageType = typename fx_rep::StorageType;
33 
34   StorageType value;
35 
36   static_assert(fx_rep::FRACTION_LEN > 0);
37 
38   static constexpr size_t FRACTION_OFFSET = 0; // Just for completeness
39   static constexpr size_t INTEGRAL_OFFSET =
40       fx_rep::INTEGRAL_LEN == 0 ? 0 : fx_rep::FRACTION_LEN;
41   static constexpr size_t SIGN_OFFSET =
42       fx_rep::SIGN_LEN == 0
43           ? 0
44           : ((sizeof(StorageType) * CHAR_BIT) - fx_rep::SIGN_LEN);
45 
46   static constexpr StorageType FRACTION_MASK =
47       mask_trailing_ones<StorageType, fx_rep::FRACTION_LEN>()
48       << FRACTION_OFFSET;
49   static constexpr StorageType INTEGRAL_MASK =
50       mask_trailing_ones<StorageType, fx_rep::INTEGRAL_LEN>()
51       << INTEGRAL_OFFSET;
52   static constexpr StorageType SIGN_MASK =
53       (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET);
54 
55   // mask for <integral | fraction>
56   static constexpr StorageType VALUE_MASK = INTEGRAL_MASK | FRACTION_MASK;
57 
58   // mask for <sign | integral | fraction>
59   static constexpr StorageType TOTAL_MASK = SIGN_MASK | VALUE_MASK;
60 
61 public:
62   LIBC_INLINE constexpr FXBits() = default;
63 
FXBitsFXBits64   template <typename XType> LIBC_INLINE constexpr explicit FXBits(XType x) {
65     using Unqual = typename cpp::remove_cv_t<XType>;
66     if constexpr (cpp::is_same_v<Unqual, T>) {
67       value = cpp::bit_cast<StorageType>(x);
68     } else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
69       value = x;
70     } else {
71       // We don't want accidental type promotions/conversions, so we require
72       // exact type match.
73       static_assert(cpp::always_false<XType>);
74     }
75   }
76 
get_fractionFXBits77   LIBC_INLINE constexpr StorageType get_fraction() {
78     return (value & FRACTION_MASK) >> FRACTION_OFFSET;
79   }
80 
get_integralFXBits81   LIBC_INLINE constexpr StorageType get_integral() {
82     return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET;
83   }
84 
85   // returns complete bitstring representation the fixed point number
86   // the bitstring is of the form: padding | sign | integral | fraction
get_bitsFXBits87   LIBC_INLINE constexpr StorageType get_bits() {
88     return (value & TOTAL_MASK) >> FRACTION_OFFSET;
89   }
90 
91   // TODO: replace bool with Sign
get_signFXBits92   LIBC_INLINE constexpr bool get_sign() {
93     return static_cast<bool>((value & SIGN_MASK) >> SIGN_OFFSET);
94   }
95 
96   // This represents the effective negative exponent applied to this number
get_exponentFXBits97   LIBC_INLINE constexpr int get_exponent() { return fx_rep::FRACTION_LEN; }
98 
set_fractionFXBits99   LIBC_INLINE constexpr void set_fraction(StorageType fraction) {
100     value = (value & (~FRACTION_MASK)) |
101             ((fraction << FRACTION_OFFSET) & FRACTION_MASK);
102   }
103 
set_integralFXBits104   LIBC_INLINE constexpr void set_integral(StorageType integral) {
105     value = (value & (~INTEGRAL_MASK)) |
106             ((integral << INTEGRAL_OFFSET) & INTEGRAL_MASK);
107   }
108 
109   // TODO: replace bool with Sign
set_signFXBits110   LIBC_INLINE constexpr void set_sign(bool sign) {
111     value = (value & (~SIGN_MASK)) |
112             ((static_cast<StorageType>(sign) << SIGN_OFFSET) & SIGN_MASK);
113   }
114 
get_valFXBits115   LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(value); }
116 };
117 
118 // Bit-wise operations are not available for fixed point types yet.
119 template <typename T>
120 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_and(T x,T y)121 bit_and(T x, T y) {
122   using BitType = typename FXRep<T>::StorageType;
123   BitType x_bit = cpp::bit_cast<BitType>(x);
124   BitType y_bit = cpp::bit_cast<BitType>(y);
125   // For some reason, bit_cast cannot deduce BitType from the input.
126   return cpp::bit_cast<T, BitType>(x_bit & y_bit);
127 }
128 
129 template <typename T>
130 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_or(T x,T y)131 bit_or(T x, T y) {
132   using BitType = typename FXRep<T>::StorageType;
133   BitType x_bit = cpp::bit_cast<BitType>(x);
134   BitType y_bit = cpp::bit_cast<BitType>(y);
135   // For some reason, bit_cast cannot deduce BitType from the input.
136   return cpp::bit_cast<T, BitType>(x_bit | y_bit);
137 }
138 
139 template <typename T>
140 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_not(T x)141 bit_not(T x) {
142   using BitType = typename FXRep<T>::StorageType;
143   BitType x_bit = cpp::bit_cast<BitType>(x);
144   // For some reason, bit_cast cannot deduce BitType from the input.
145   return cpp::bit_cast<T, BitType>(static_cast<BitType>(~x_bit));
146 }
147 
abs(T x)148 template <typename T> LIBC_INLINE constexpr T abs(T x) {
149   using FXRep = FXRep<T>;
150   if constexpr (FXRep::SIGN_LEN == 0)
151     return x;
152   else {
153     if (LIBC_UNLIKELY(x == FXRep::MIN()))
154       return FXRep::MAX();
155     return (x < FXRep::ZERO() ? -x : x);
156   }
157 }
158 
159 // Round-to-nearest, tie-to-(+Inf)
round(T x,int n)160 template <typename T> LIBC_INLINE constexpr T round(T x, int n) {
161   using FXRep = FXRep<T>;
162   if (LIBC_UNLIKELY(n < 0))
163     n = 0;
164   if (LIBC_UNLIKELY(n >= FXRep::FRACTION_LEN))
165     return x;
166 
167   T round_bit = FXRep::EPS() << (FXRep::FRACTION_LEN - n - 1);
168   // Check for overflow.
169   if (LIBC_UNLIKELY(FXRep::MAX() - round_bit < x))
170     return FXRep::MAX();
171 
172   T all_ones = bit_not(FXRep::ZERO());
173 
174   int shift = FXRep::FRACTION_LEN - n;
175   T rounding_mask =
176       (shift == FXRep::TOTAL_LEN) ? FXRep::ZERO() : (all_ones << shift);
177   return bit_and((x + round_bit), rounding_mask);
178 }
179 
180 // count leading sign bits
181 // TODO: support fixed_point_padding
182 template <typename T>
183 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, int>
countls(T f)184 countls(T f) {
185   using FXRep = FXRep<T>;
186   using BitType = typename FXRep::StorageType;
187   using FXBits = FXBits<T>;
188 
189   if constexpr (FXRep::SIGN_LEN > 0) {
190     if (f < 0)
191       f = bit_not(f);
192   }
193 
194   BitType value_bits = FXBits(f).get_bits();
195   return cpp::countl_zero(value_bits) - FXRep::SIGN_LEN;
196 }
197 
198 // fixed-point to integer conversion
199 template <typename T, typename XType>
200 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, XType>
bitsfx(T f)201 bitsfx(T f) {
202   return cpp::bit_cast<XType, T>(f);
203 }
204 
205 // divide the two fixed-point types and return an integer result
206 template <typename T, typename XType>
207 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, XType>
idiv(T x,T y)208 idiv(T x, T y) {
209   using FXBits = FXBits<T>;
210   using FXRep = FXRep<T>;
211   using CompType = typename FXRep::CompType;
212 
213   // If the value of the second operand of the / operator is zero, the
214   // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16
215   LIBC_CRASH_ON_VALUE(y, FXRep::ZERO());
216 
217   CompType x_comp = static_cast<CompType>(FXBits(x).get_bits());
218   CompType y_comp = static_cast<CompType>(FXBits(y).get_bits());
219 
220   // If an integer result of one of these functions overflows, the behavior is
221   // undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16
222   CompType result = x_comp / y_comp;
223 
224   return static_cast<XType>(result);
225 }
226 
227 } // namespace fixed_point
228 } // namespace LIBC_NAMESPACE_DECL
229 
230 #endif // LIBC_COMPILER_HAS_FIXED_POINT
231 
232 #endif // LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H
233