• 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/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/optimization.h" // LIBC_UNLIKELY
19 #include "src/__support/math_extras.h"
20 
21 #include "fx_rep.h"
22 
23 #ifdef LIBC_COMPILER_HAS_FIXED_POINT
24 
25 namespace LIBC_NAMESPACE_DECL {
26 namespace fixed_point {
27 
28 template <typename T> struct FXBits {
29 private:
30   using fx_rep = FXRep<T>;
31   using StorageType = typename fx_rep::StorageType;
32 
33   StorageType value;
34 
35   static_assert(fx_rep::FRACTION_LEN > 0);
36 
37   static constexpr size_t FRACTION_OFFSET = 0; // Just for completeness
38   static constexpr size_t INTEGRAL_OFFSET =
39       fx_rep::INTEGRAL_LEN == 0 ? 0 : fx_rep::FRACTION_LEN;
40   static constexpr size_t SIGN_OFFSET =
41       fx_rep::SIGN_LEN == 0
42           ? 0
43           : ((sizeof(StorageType) * CHAR_BIT) - fx_rep::SIGN_LEN);
44 
45   static constexpr StorageType FRACTION_MASK =
46       mask_trailing_ones<StorageType, fx_rep::FRACTION_LEN>()
47       << FRACTION_OFFSET;
48   static constexpr StorageType INTEGRAL_MASK =
49       mask_trailing_ones<StorageType, fx_rep::INTEGRAL_LEN>()
50       << INTEGRAL_OFFSET;
51   static constexpr StorageType SIGN_MASK =
52       (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET);
53 
54   // mask for <integral | fraction>
55   static constexpr StorageType VALUE_MASK = INTEGRAL_MASK | FRACTION_MASK;
56 
57   // mask for <sign | integral | fraction>
58   static constexpr StorageType TOTAL_MASK = SIGN_MASK | VALUE_MASK;
59 
60 public:
61   LIBC_INLINE constexpr FXBits() = default;
62 
FXBitsFXBits63   template <typename XType> LIBC_INLINE constexpr explicit FXBits(XType x) {
64     using Unqual = typename cpp::remove_cv_t<XType>;
65     if constexpr (cpp::is_same_v<Unqual, T>) {
66       value = cpp::bit_cast<StorageType>(x);
67     } else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
68       value = x;
69     } else {
70       // We don't want accidental type promotions/conversions, so we require
71       // exact type match.
72       static_assert(cpp::always_false<XType>);
73     }
74   }
75 
get_fractionFXBits76   LIBC_INLINE constexpr StorageType get_fraction() {
77     return (value & FRACTION_MASK) >> FRACTION_OFFSET;
78   }
79 
get_integralFXBits80   LIBC_INLINE constexpr StorageType get_integral() {
81     return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET;
82   }
83 
84   // returns complete bitstring representation the fixed point number
85   // the bitstring is of the form: padding | sign | integral | fraction
get_bitsFXBits86   LIBC_INLINE constexpr StorageType get_bits() {
87     return (value & TOTAL_MASK) >> FRACTION_OFFSET;
88   }
89 
90   // TODO: replace bool with Sign
get_signFXBits91   LIBC_INLINE constexpr bool get_sign() {
92     return static_cast<bool>((value & SIGN_MASK) >> SIGN_OFFSET);
93   }
94 
95   // This represents the effective negative exponent applied to this number
get_exponentFXBits96   LIBC_INLINE constexpr int get_exponent() { return fx_rep::FRACTION_LEN; }
97 
set_fractionFXBits98   LIBC_INLINE constexpr void set_fraction(StorageType fraction) {
99     value = (value & (~FRACTION_MASK)) |
100             ((fraction << FRACTION_OFFSET) & FRACTION_MASK);
101   }
102 
set_integralFXBits103   LIBC_INLINE constexpr void set_integral(StorageType integral) {
104     value = (value & (~INTEGRAL_MASK)) |
105             ((integral << INTEGRAL_OFFSET) & INTEGRAL_MASK);
106   }
107 
108   // TODO: replace bool with Sign
set_signFXBits109   LIBC_INLINE constexpr void set_sign(bool sign) {
110     value = (value & (~SIGN_MASK)) |
111             ((static_cast<StorageType>(sign) << SIGN_OFFSET) & SIGN_MASK);
112   }
113 
get_valFXBits114   LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(value); }
115 };
116 
117 // Bit-wise operations are not available for fixed point types yet.
118 template <typename T>
119 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_and(T x,T y)120 bit_and(T x, T y) {
121   using BitType = typename FXRep<T>::StorageType;
122   BitType x_bit = cpp::bit_cast<BitType>(x);
123   BitType y_bit = cpp::bit_cast<BitType>(y);
124   // For some reason, bit_cast cannot deduce BitType from the input.
125   return cpp::bit_cast<T, BitType>(x_bit & y_bit);
126 }
127 
128 template <typename T>
129 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_or(T x,T y)130 bit_or(T x, T y) {
131   using BitType = typename FXRep<T>::StorageType;
132   BitType x_bit = cpp::bit_cast<BitType>(x);
133   BitType y_bit = cpp::bit_cast<BitType>(y);
134   // For some reason, bit_cast cannot deduce BitType from the input.
135   return cpp::bit_cast<T, BitType>(x_bit | y_bit);
136 }
137 
138 template <typename T>
139 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
bit_not(T x)140 bit_not(T x) {
141   using BitType = typename FXRep<T>::StorageType;
142   BitType x_bit = cpp::bit_cast<BitType>(x);
143   // For some reason, bit_cast cannot deduce BitType from the input.
144   return cpp::bit_cast<T, BitType>(static_cast<BitType>(~x_bit));
145 }
146 
abs(T x)147 template <typename T> LIBC_INLINE constexpr T abs(T x) {
148   using FXRep = FXRep<T>;
149   if constexpr (FXRep::SIGN_LEN == 0)
150     return x;
151   else {
152     if (LIBC_UNLIKELY(x == FXRep::MIN()))
153       return FXRep::MAX();
154     return (x < FXRep::ZERO() ? -x : x);
155   }
156 }
157 
158 // Round-to-nearest, tie-to-(+Inf)
round(T x,int n)159 template <typename T> LIBC_INLINE constexpr T round(T x, int n) {
160   using FXRep = FXRep<T>;
161   if (LIBC_UNLIKELY(n < 0))
162     n = 0;
163   if (LIBC_UNLIKELY(n >= FXRep::FRACTION_LEN))
164     return x;
165 
166   T round_bit = FXRep::EPS() << (FXRep::FRACTION_LEN - n - 1);
167   // Check for overflow.
168   if (LIBC_UNLIKELY(FXRep::MAX() - round_bit < x))
169     return FXRep::MAX();
170 
171   T all_ones = bit_not(FXRep::ZERO());
172 
173   int shift = FXRep::FRACTION_LEN - n;
174   T rounding_mask =
175       (shift == FXRep::TOTAL_LEN) ? FXRep::ZERO() : (all_ones << shift);
176   return bit_and((x + round_bit), rounding_mask);
177 }
178 
179 // count leading sign bits
180 // TODO: support fixed_point_padding
181 template <typename T>
182 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, int>
countls(T f)183 countls(T f) {
184   using FXRep = FXRep<T>;
185   using BitType = typename FXRep::StorageType;
186   using FXBits = FXBits<T>;
187 
188   if constexpr (FXRep::SIGN_LEN > 0) {
189     if (f < 0)
190       f = bit_not(f);
191   }
192 
193   BitType value_bits = FXBits(f).get_bits();
194   return cpp::countl_zero(value_bits) - FXRep::SIGN_LEN;
195 }
196 
197 // fixed-point to integer conversion
198 template <typename T, typename XType>
199 LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, XType>
bitsfx(T f)200 bitsfx(T f) {
201   return cpp::bit_cast<XType, T>(f);
202 }
203 
204 } // namespace fixed_point
205 } // namespace LIBC_NAMESPACE_DECL
206 
207 #endif // LIBC_COMPILER_HAS_FIXED_POINT
208 
209 #endif // LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H
210