1 //===-- Utility class to manipulate fixed point numbers. --*- C++ -*-=========//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H
10 #define LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H
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"
22 #ifdef LIBC_COMPILER_HAS_FIXED_POINT
24 namespace LIBC_NAMESPACE_DECL
{
25 namespace fixed_point
{
27 template <typename T
> struct FXBits
{
29 using fx_rep
= FXRep
<T
>;
30 using StorageType
= typename
fx_rep::StorageType
;
34 static_assert(fx_rep::FRACTION_LEN
> 0);
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
=
42 : ((sizeof(StorageType
) * CHAR_BIT
) - fx_rep::SIGN_LEN
);
44 static constexpr StorageType FRACTION_MASK
=
45 mask_trailing_ones
<StorageType
, fx_rep::FRACTION_LEN
>()
47 static constexpr StorageType INTEGRAL_MASK
=
48 mask_trailing_ones
<StorageType
, fx_rep::INTEGRAL_LEN
>()
50 static constexpr StorageType SIGN_MASK
=
51 (fx_rep::SIGN_LEN
== 0 ? 0 : StorageType(1) << SIGN_OFFSET
);
54 LIBC_INLINE
constexpr FXBits() = default;
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
>) {
63 // We don't want accidental type promotions/conversions, so we require
65 static_assert(cpp::always_false
<XType
>);
69 LIBC_INLINE
constexpr StorageType
get_fraction() {
70 return (value
& FRACTION_MASK
) >> FRACTION_OFFSET
;
73 LIBC_INLINE
constexpr StorageType
get_integral() {
74 return (value
& INTEGRAL_MASK
) >> INTEGRAL_OFFSET
;
77 // TODO: replace bool with Sign
78 LIBC_INLINE
constexpr bool get_sign() {
79 return static_cast<bool>((value
& SIGN_MASK
) >> SIGN_OFFSET
);
82 // This represents the effective negative exponent applied to this number
83 LIBC_INLINE
constexpr int get_exponent() { return fx_rep::FRACTION_LEN
; }
85 LIBC_INLINE
constexpr void set_fraction(StorageType fraction
) {
86 value
= (value
& (~FRACTION_MASK
)) |
87 ((fraction
<< FRACTION_OFFSET
) & FRACTION_MASK
);
90 LIBC_INLINE
constexpr void set_integral(StorageType integral
) {
91 value
= (value
& (~INTEGRAL_MASK
)) |
92 ((integral
<< INTEGRAL_OFFSET
) & INTEGRAL_MASK
);
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
);
101 LIBC_INLINE
constexpr T
get_val() const { return cpp::bit_cast
<T
>(value
); }
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
>
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
);
115 template <typename T
>
116 LIBC_INLINE
constexpr cpp::enable_if_t
<cpp::is_fixed_point_v
<T
>, T
>
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
);
125 template <typename T
>
126 LIBC_INLINE
constexpr cpp::enable_if_t
<cpp::is_fixed_point_v
<T
>, T
>
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
));
134 template <typename T
> LIBC_INLINE
constexpr T
abs(T x
) {
135 using FXRep
= FXRep
<T
>;
136 if constexpr (FXRep::SIGN_LEN
== 0)
139 if (LIBC_UNLIKELY(x
== FXRep::MIN()))
141 return (x
< FXRep::ZERO() ? -x
: x
);
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))
150 if (LIBC_UNLIKELY(n
>= FXRep::FRACTION_LEN
))
153 T round_bit
= FXRep::EPS() << (FXRep::FRACTION_LEN
- n
- 1);
154 // Check for overflow.
155 if (LIBC_UNLIKELY(FXRep::MAX() - round_bit
< x
))
158 T all_ones
= bit_not(FXRep::ZERO());
160 int shift
= FXRep::FRACTION_LEN
- n
;
162 (shift
== FXRep::TOTAL_LEN
) ? FXRep::ZERO() : (all_ones
<< shift
);
163 return bit_and((x
+ round_bit
), rounding_mask
);
166 } // namespace fixed_point
167 } // namespace LIBC_NAMESPACE_DECL
169 #endif // LIBC_COMPILER_HAS_FIXED_POINT
171 #endif // LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H