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
FXBitsFXBits56 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
get_fractionFXBits69 LIBC_INLINE constexpr StorageType get_fraction() {
70 return (value & FRACTION_MASK) >> FRACTION_OFFSET;
71 }
72
get_integralFXBits73 LIBC_INLINE constexpr StorageType get_integral() {
74 return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET;
75 }
76
77 // TODO: replace bool with Sign
get_signFXBits78 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
get_exponentFXBits83 LIBC_INLINE constexpr int get_exponent() { return fx_rep::FRACTION_LEN; }
84
set_fractionFXBits85 LIBC_INLINE constexpr void set_fraction(StorageType fraction) {
86 value = (value & (~FRACTION_MASK)) |
87 ((fraction << FRACTION_OFFSET) & FRACTION_MASK);
88 }
89
set_integralFXBits90 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
set_signFXBits96 LIBC_INLINE constexpr void set_sign(bool sign) {
97 value = (value & (~SIGN_MASK)) |
98 ((static_cast<StorageType>(sign) << SIGN_OFFSET) & SIGN_MASK);
99 }
100
get_valFXBits101 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>
bit_and(T x,T y)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>
bit_or(T x,T y)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>
bit_not(T x)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
abs(T x)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)
round(T x,int n)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