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