• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/optimization.h" // LIBC_UNLIKELY
17 #include "src/__support/math_extras.h"
18 
19 #include "fx_rep.h"
20 
21 #ifdef LIBC_COMPILER_HAS_FIXED_POINT
22 
23 namespace LIBC_NAMESPACE::fixed_point {
24 
25 template <typename T> struct FXBits {
26 private:
27   using fx_rep = FXRep<T>;
28   using StorageType = typename fx_rep::StorageType;
29 
30   StorageType value;
31 
32   static_assert(fx_rep::FRACTION_LEN > 0);
33 
34   static constexpr size_t FRACTION_OFFSET = 0; // Just for completeness
35   static constexpr size_t INTEGRAL_OFFSET =
36       fx_rep::INTEGRAL_LEN == 0 ? 0 : fx_rep::FRACTION_LEN;
37   static constexpr size_t SIGN_OFFSET =
38       fx_rep::SIGN_LEN == 0
39           ? 0
40           : ((sizeof(StorageType) * CHAR_BIT) - fx_rep::SIGN_LEN);
41 
42   static constexpr StorageType FRACTION_MASK =
43       mask_trailing_ones<StorageType, fx_rep::FRACTION_LEN>()
44       << FRACTION_OFFSET;
45   static constexpr StorageType INTEGRAL_MASK =
46       mask_trailing_ones<StorageType, fx_rep::INTEGRAL_LEN>()
47       << INTEGRAL_OFFSET;
48   static constexpr StorageType SIGN_MASK =
49       (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET);
50 
51 public:
52   LIBC_INLINE constexpr FXBits() = default;
53 
FXBitsFXBits54   template <typename XType> LIBC_INLINE constexpr explicit FXBits(XType x) {
55     using Unqual = typename cpp::remove_cv_t<XType>;
56     if constexpr (cpp::is_same_v<Unqual, T>) {
57       value = cpp::bit_cast<StorageType>(x);
58     } else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
59       value = x;
60     } else {
61       // We don't want accidental type promotions/conversions, so we require
62       // exact type match.
63       static_assert(cpp::always_false<XType>);
64     }
65   }
66 
get_fractionFXBits67   LIBC_INLINE constexpr StorageType get_fraction() {
68     return (value & FRACTION_MASK) >> FRACTION_OFFSET;
69   }
70 
get_integralFXBits71   LIBC_INLINE constexpr StorageType get_integral() {
72     return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET;
73   }
74 
75   // TODO: replace bool with Sign
get_signFXBits76   LIBC_INLINE constexpr bool get_sign() {
77     return static_cast<bool>((value & SIGN_MASK) >> SIGN_OFFSET);
78   }
79 
80   // This represents the effective negative exponent applied to this number
get_exponentFXBits81   LIBC_INLINE constexpr int get_exponent() { return fx_rep::FRACTION_LEN; }
82 
set_fractionFXBits83   LIBC_INLINE constexpr void set_fraction(StorageType fraction) {
84     value = (value & (~FRACTION_MASK)) |
85             ((fraction << FRACTION_OFFSET) & FRACTION_MASK);
86   }
87 
set_integralFXBits88   LIBC_INLINE constexpr void set_integral(StorageType integral) {
89     value = (value & (~INTEGRAL_MASK)) |
90             ((integral << INTEGRAL_OFFSET) & INTEGRAL_MASK);
91   }
92 
93   // TODO: replace bool with Sign
set_signFXBits94   LIBC_INLINE constexpr void set_sign(bool sign) {
95     value = (value & (~SIGN_MASK)) |
96             ((static_cast<StorageType>(sign) << SIGN_OFFSET) & SIGN_MASK);
97   }
98 
get_valFXBits99   LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(value); }
100 };
101 
102 // Bit-wise operations are not available for fixed point types yet.
103 template <typename T>
104 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_and(T x,T y)105 bit_and(T x, T y) {
106   using BitType = typename FXRep<T>::StorageType;
107   BitType x_bit = cpp::bit_cast<BitType>(x);
108   BitType y_bit = cpp::bit_cast<BitType>(y);
109   // For some reason, bit_cast cannot deduce BitType from the input.
110   return cpp::bit_cast<T, BitType>(x_bit & y_bit);
111 }
112 
113 template <typename T>
114 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_or(T x,T y)115 bit_or(T x, T y) {
116   using BitType = typename FXRep<T>::StorageType;
117   BitType x_bit = cpp::bit_cast<BitType>(x);
118   BitType y_bit = cpp::bit_cast<BitType>(y);
119   // For some reason, bit_cast cannot deduce BitType from the input.
120   return cpp::bit_cast<T, BitType>(x_bit | y_bit);
121 }
122 
123 template <typename T>
124 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_not(T x)125 bit_not(T x) {
126   using BitType = typename FXRep<T>::StorageType;
127   BitType x_bit = cpp::bit_cast<BitType>(x);
128   // For some reason, bit_cast cannot deduce BitType from the input.
129   return cpp::bit_cast<T, BitType>(static_cast<BitType>(~x_bit));
130 }
131 
abs(T x)132 template <typename T> LIBC_INLINE constexpr T abs(T x) {
133   using FXRep = FXRep<T>;
134   if constexpr (FXRep::SIGN_LEN == 0)
135     return x;
136   else {
137     if (LIBC_UNLIKELY(x == FXRep::MIN()))
138       return FXRep::MAX();
139     return (x < FXRep::ZERO() ? -x : x);
140   }
141 }
142 
143 // Round-to-nearest, tie-to-(+Inf)
round(T x,int n)144 template <typename T> LIBC_INLINE constexpr T round(T x, int n) {
145   using FXRep = FXRep<T>;
146   if (LIBC_UNLIKELY(n < 0))
147     n = 0;
148   if (LIBC_UNLIKELY(n >= FXRep::FRACTION_LEN))
149     return x;
150 
151   T round_bit = FXRep::EPS() << (FXRep::FRACTION_LEN - n - 1);
152   // Check for overflow.
153   if (LIBC_UNLIKELY(FXRep::MAX() - round_bit < x))
154     return FXRep::MAX();
155 
156   T all_ones = bit_not(FXRep::ZERO());
157 
158   int shift = FXRep::FRACTION_LEN - n;
159   T rounding_mask =
160       (shift == FXRep::TOTAL_LEN) ? FXRep::ZERO() : (all_ones << shift);
161   return bit_and((x + round_bit), rounding_mask);
162 }
163 
164 } // namespace LIBC_NAMESPACE::fixed_point
165 
166 #endif // LIBC_COMPILER_HAS_FIXED_POINT
167 
168 #endif // LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H
169