xref: /llvm-project/libc/src/__support/fixed_point/fx_bits.h (revision c11b6e80b6121ebbec6fdc32e51938b6a13ef104)
12c45bda8Slntue //===-- Utility class to manipulate fixed point numbers. --*- C++ -*-=========//
22c45bda8Slntue //
32c45bda8Slntue // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42c45bda8Slntue // See https://llvm.org/LICENSE.txt for license information.
52c45bda8Slntue // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62c45bda8Slntue //
72c45bda8Slntue //===----------------------------------------------------------------------===//
82c45bda8Slntue 
9330793c9SNick Desaulniers #ifndef LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H
10330793c9SNick Desaulniers #define LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H
112c45bda8Slntue 
1273aab2f6Slntue #include "include/llvm-libc-macros/stdfix-macros.h"
13f01ed3bcSlntue #include "src/__support/CPP/bit.h"
14f01ed3bcSlntue #include "src/__support/CPP/type_traits.h"
152c45bda8Slntue #include "src/__support/macros/attributes.h" // LIBC_INLINE
16*5ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
172c45bda8Slntue #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
185b079af1SMichael Jones #include "src/__support/math_extras.h"
192c45bda8Slntue 
202c45bda8Slntue #include "fx_rep.h"
212c45bda8Slntue 
222c45bda8Slntue #ifdef LIBC_COMPILER_HAS_FIXED_POINT
232c45bda8Slntue 
24*5ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
25*5ff3ff33SPetr Hosek namespace fixed_point {
262c45bda8Slntue 
275b079af1SMichael Jones template <typename T> struct FXBits {
285b079af1SMichael Jones private:
295b079af1SMichael Jones   using fx_rep = FXRep<T>;
305b079af1SMichael Jones   using StorageType = typename fx_rep::StorageType;
315b079af1SMichael Jones 
325b079af1SMichael Jones   StorageType value;
335b079af1SMichael Jones 
345b079af1SMichael Jones   static_assert(fx_rep::FRACTION_LEN > 0);
355b079af1SMichael Jones 
365b079af1SMichael Jones   static constexpr size_t FRACTION_OFFSET = 0; // Just for completeness
375b079af1SMichael Jones   static constexpr size_t INTEGRAL_OFFSET =
385b079af1SMichael Jones       fx_rep::INTEGRAL_LEN == 0 ? 0 : fx_rep::FRACTION_LEN;
395b079af1SMichael Jones   static constexpr size_t SIGN_OFFSET =
405b079af1SMichael Jones       fx_rep::SIGN_LEN == 0
415b079af1SMichael Jones           ? 0
425b079af1SMichael Jones           : ((sizeof(StorageType) * CHAR_BIT) - fx_rep::SIGN_LEN);
435b079af1SMichael Jones 
445b079af1SMichael Jones   static constexpr StorageType FRACTION_MASK =
455b079af1SMichael Jones       mask_trailing_ones<StorageType, fx_rep::FRACTION_LEN>()
465b079af1SMichael Jones       << FRACTION_OFFSET;
475b079af1SMichael Jones   static constexpr StorageType INTEGRAL_MASK =
485b079af1SMichael Jones       mask_trailing_ones<StorageType, fx_rep::INTEGRAL_LEN>()
495b079af1SMichael Jones       << INTEGRAL_OFFSET;
505b079af1SMichael Jones   static constexpr StorageType SIGN_MASK =
515b079af1SMichael Jones       (fx_rep::SIGN_LEN == 0 ? 0 : StorageType(1) << SIGN_OFFSET);
525b079af1SMichael Jones 
535b079af1SMichael Jones public:
545b079af1SMichael Jones   LIBC_INLINE constexpr FXBits() = default;
555b079af1SMichael Jones 
565b079af1SMichael Jones   template <typename XType> LIBC_INLINE constexpr explicit FXBits(XType x) {
575b079af1SMichael Jones     using Unqual = typename cpp::remove_cv_t<XType>;
585b079af1SMichael Jones     if constexpr (cpp::is_same_v<Unqual, T>) {
595b079af1SMichael Jones       value = cpp::bit_cast<StorageType>(x);
605b079af1SMichael Jones     } else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
615b079af1SMichael Jones       value = x;
625b079af1SMichael Jones     } else {
635b079af1SMichael Jones       // We don't want accidental type promotions/conversions, so we require
645b079af1SMichael Jones       // exact type match.
655b079af1SMichael Jones       static_assert(cpp::always_false<XType>);
665b079af1SMichael Jones     }
675b079af1SMichael Jones   }
685b079af1SMichael Jones 
695b079af1SMichael Jones   LIBC_INLINE constexpr StorageType get_fraction() {
705b079af1SMichael Jones     return (value & FRACTION_MASK) >> FRACTION_OFFSET;
715b079af1SMichael Jones   }
725b079af1SMichael Jones 
735b079af1SMichael Jones   LIBC_INLINE constexpr StorageType get_integral() {
745b079af1SMichael Jones     return (value & INTEGRAL_MASK) >> INTEGRAL_OFFSET;
755b079af1SMichael Jones   }
765b079af1SMichael Jones 
775b079af1SMichael Jones   // TODO: replace bool with Sign
785b079af1SMichael Jones   LIBC_INLINE constexpr bool get_sign() {
795b079af1SMichael Jones     return static_cast<bool>((value & SIGN_MASK) >> SIGN_OFFSET);
805b079af1SMichael Jones   }
815b079af1SMichael Jones 
825b079af1SMichael Jones   // This represents the effective negative exponent applied to this number
835b079af1SMichael Jones   LIBC_INLINE constexpr int get_exponent() { return fx_rep::FRACTION_LEN; }
845b079af1SMichael Jones 
855b079af1SMichael Jones   LIBC_INLINE constexpr void set_fraction(StorageType fraction) {
865b079af1SMichael Jones     value = (value & (~FRACTION_MASK)) |
875b079af1SMichael Jones             ((fraction << FRACTION_OFFSET) & FRACTION_MASK);
885b079af1SMichael Jones   }
895b079af1SMichael Jones 
905b079af1SMichael Jones   LIBC_INLINE constexpr void set_integral(StorageType integral) {
915b079af1SMichael Jones     value = (value & (~INTEGRAL_MASK)) |
925b079af1SMichael Jones             ((integral << INTEGRAL_OFFSET) & INTEGRAL_MASK);
935b079af1SMichael Jones   }
945b079af1SMichael Jones 
955b079af1SMichael Jones   // TODO: replace bool with Sign
965b079af1SMichael Jones   LIBC_INLINE constexpr void set_sign(bool sign) {
975b079af1SMichael Jones     value = (value & (~SIGN_MASK)) |
985b079af1SMichael Jones             ((static_cast<StorageType>(sign) << SIGN_OFFSET) & SIGN_MASK);
995b079af1SMichael Jones   }
1005b079af1SMichael Jones 
1015b079af1SMichael Jones   LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(value); }
1025b079af1SMichael Jones };
1035b079af1SMichael Jones 
104f01ed3bcSlntue // Bit-wise operations are not available for fixed point types yet.
105f01ed3bcSlntue template <typename T>
106f01ed3bcSlntue LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
107f01ed3bcSlntue bit_and(T x, T y) {
108f01ed3bcSlntue   using BitType = typename FXRep<T>::StorageType;
109f01ed3bcSlntue   BitType x_bit = cpp::bit_cast<BitType>(x);
110f01ed3bcSlntue   BitType y_bit = cpp::bit_cast<BitType>(y);
111f01ed3bcSlntue   // For some reason, bit_cast cannot deduce BitType from the input.
112f01ed3bcSlntue   return cpp::bit_cast<T, BitType>(x_bit & y_bit);
113f01ed3bcSlntue }
114f01ed3bcSlntue 
115f01ed3bcSlntue template <typename T>
116f01ed3bcSlntue LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
117f01ed3bcSlntue bit_or(T x, T y) {
118f01ed3bcSlntue   using BitType = typename FXRep<T>::StorageType;
119f01ed3bcSlntue   BitType x_bit = cpp::bit_cast<BitType>(x);
120f01ed3bcSlntue   BitType y_bit = cpp::bit_cast<BitType>(y);
121f01ed3bcSlntue   // For some reason, bit_cast cannot deduce BitType from the input.
122f01ed3bcSlntue   return cpp::bit_cast<T, BitType>(x_bit | y_bit);
123f01ed3bcSlntue }
124f01ed3bcSlntue 
125f01ed3bcSlntue template <typename T>
126f01ed3bcSlntue LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<T>, T>
127f01ed3bcSlntue bit_not(T x) {
128f01ed3bcSlntue   using BitType = typename FXRep<T>::StorageType;
129f01ed3bcSlntue   BitType x_bit = cpp::bit_cast<BitType>(x);
130f01ed3bcSlntue   // For some reason, bit_cast cannot deduce BitType from the input.
13173dfc7bbSlntue   return cpp::bit_cast<T, BitType>(static_cast<BitType>(~x_bit));
132f01ed3bcSlntue }
133f01ed3bcSlntue 
1342c45bda8Slntue template <typename T> LIBC_INLINE constexpr T abs(T x) {
1352c45bda8Slntue   using FXRep = FXRep<T>;
1362c45bda8Slntue   if constexpr (FXRep::SIGN_LEN == 0)
1372c45bda8Slntue     return x;
1382c45bda8Slntue   else {
1392c45bda8Slntue     if (LIBC_UNLIKELY(x == FXRep::MIN()))
1402c45bda8Slntue       return FXRep::MAX();
1412c45bda8Slntue     return (x < FXRep::ZERO() ? -x : x);
1422c45bda8Slntue   }
1432c45bda8Slntue }
1442c45bda8Slntue 
145f01ed3bcSlntue // Round-to-nearest, tie-to-(+Inf)
146f01ed3bcSlntue template <typename T> LIBC_INLINE constexpr T round(T x, int n) {
147f01ed3bcSlntue   using FXRep = FXRep<T>;
148f01ed3bcSlntue   if (LIBC_UNLIKELY(n < 0))
149f01ed3bcSlntue     n = 0;
150f01ed3bcSlntue   if (LIBC_UNLIKELY(n >= FXRep::FRACTION_LEN))
151f01ed3bcSlntue     return x;
152f01ed3bcSlntue 
153f01ed3bcSlntue   T round_bit = FXRep::EPS() << (FXRep::FRACTION_LEN - n - 1);
154f01ed3bcSlntue   // Check for overflow.
155f01ed3bcSlntue   if (LIBC_UNLIKELY(FXRep::MAX() - round_bit < x))
156f01ed3bcSlntue     return FXRep::MAX();
157f01ed3bcSlntue 
158f01ed3bcSlntue   T all_ones = bit_not(FXRep::ZERO());
159f01ed3bcSlntue 
160f01ed3bcSlntue   int shift = FXRep::FRACTION_LEN - n;
161f01ed3bcSlntue   T rounding_mask =
162f01ed3bcSlntue       (shift == FXRep::TOTAL_LEN) ? FXRep::ZERO() : (all_ones << shift);
163f01ed3bcSlntue   return bit_and((x + round_bit), rounding_mask);
164f01ed3bcSlntue }
165f01ed3bcSlntue 
166*5ff3ff33SPetr Hosek } // namespace fixed_point
167*5ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
1682c45bda8Slntue 
1692c45bda8Slntue #endif // LIBC_COMPILER_HAS_FIXED_POINT
1702c45bda8Slntue 
171330793c9SNick Desaulniers #endif // LLVM_LIBC_SRC___SUPPORT_FIXED_POINT_FX_BITS_H
172