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
23namespace LIBC_NAMESPACE::fixed_point {
24
25template <typename T> struct FXBits {
26private:
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
51public:
52 LIBC_INLINE constexpr FXBits() = default;
53
54 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
67 LIBC_INLINE constexpr StorageType get_fraction() {
68 return (value & FRACTION_MASK) >> FRACTION_OFFSET;
69 }
70
71 LIBC_INLINE constexpr StorageType get_integral() {
72 return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET;
73 }
74
75 // TODO: replace bool with Sign
76 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
81 LIBC_INLINE constexpr int get_exponent() { return fx_rep::FRACTION_LEN; }
82
83 LIBC_INLINE constexpr void set_fraction(StorageType fraction) {
84 value = (value & (~FRACTION_MASK)) |
85 ((fraction << FRACTION_OFFSET) & FRACTION_MASK);
86 }
87
88 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
94 LIBC_INLINE constexpr void set_sign(bool sign) {
95 value = (value & (~SIGN_MASK)) |
96 ((static_cast<StorageType>(sign) << SIGN_OFFSET) & SIGN_MASK);
97 }
98
99 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.
103template <typename T>
104LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
105bit_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
113template <typename T>
114LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
115bit_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
123template <typename T>
124LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
125bit_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
132template <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)
144template <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

source code of libc/src/__support/fixed_point/fx_bits.h