//===-- Basic operations on floating point numbers --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H #include "FEnvImpl.h" #include "FPBits.h" #include "dyadic_float.h" #include "src/__support/CPP/type_traits.h" #include "src/__support/big_int.h" #include "src/__support/common.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY #include "src/__support/macros/properties/architectures.h" #include "src/__support/macros/properties/types.h" #include "src/__support/uint128.h" namespace LIBC_NAMESPACE_DECL { namespace fputil { template , int> = 0> LIBC_INLINE T abs(T x) { return FPBits(x).abs().get_val(); } namespace internal { template LIBC_INLINE cpp::enable_if_t, T> max(T x, T y) { FPBits x_bits(x); FPBits y_bits(y); // To make sure that fmax(+0, -0) == +0 == fmax(-0, +0), whenever x and y // have different signs and both are not NaNs, we return the number with // positive sign. if (x_bits.sign() != y_bits.sign()) return x_bits.is_pos() ? x : y; return x > y ? x : y; } #ifdef LIBC_TYPES_HAS_FLOAT16 #if defined(__LIBC_USE_BUILTIN_FMAXF16_FMINF16) template <> LIBC_INLINE float16 max(float16 x, float16 y) { return __builtin_fmaxf16(x, y); } #elif !defined(LIBC_TARGET_ARCH_IS_AARCH64) template <> LIBC_INLINE float16 max(float16 x, float16 y) { FPBits x_bits(x); FPBits y_bits(y); int16_t xi = static_cast(x_bits.uintval()); int16_t yi = static_cast(y_bits.uintval()); return ((xi > yi) != (xi < 0 && yi < 0)) ? x : y; } #endif #endif // LIBC_TYPES_HAS_FLOAT16 #if defined(__LIBC_USE_BUILTIN_FMAX_FMIN) && !defined(LIBC_TARGET_ARCH_IS_X86) template <> LIBC_INLINE float max(float x, float y) { return __builtin_fmaxf(x, y); } template <> LIBC_INLINE double max(double x, double y) { return __builtin_fmax(x, y); } #endif template LIBC_INLINE cpp::enable_if_t, T> min(T x, T y) { FPBits x_bits(x); FPBits y_bits(y); // To make sure that fmin(+0, -0) == -0 == fmin(-0, +0), whenever x and y have // different signs and both are not NaNs, we return the number with negative // sign. if (x_bits.sign() != y_bits.sign()) return x_bits.is_neg() ? x : y; return x < y ? x : y; } #ifdef LIBC_TYPES_HAS_FLOAT16 #if defined(__LIBC_USE_BUILTIN_FMAXF16_FMINF16) template <> LIBC_INLINE float16 min(float16 x, float16 y) { return __builtin_fminf16(x, y); } #elif !defined(LIBC_TARGET_ARCH_IS_AARCH64) template <> LIBC_INLINE float16 min(float16 x, float16 y) { FPBits x_bits(x); FPBits y_bits(y); int16_t xi = static_cast(x_bits.uintval()); int16_t yi = static_cast(y_bits.uintval()); return ((xi < yi) != (xi < 0 && yi < 0)) ? x : y; } #endif #endif // LIBC_TYPES_HAS_FLOAT16 #if defined(__LIBC_USE_BUILTIN_FMAX_FMIN) && !defined(LIBC_TARGET_ARCH_IS_X86) template <> LIBC_INLINE float min(float x, float y) { return __builtin_fminf(x, y); } template <> LIBC_INLINE double min(double x, double y) { return __builtin_fmin(x, y); } #endif } // namespace internal template , int> = 0> LIBC_INLINE T fmin(T x, T y) { const FPBits bitx(x), bity(y); if (bitx.is_nan()) return y; if (bity.is_nan()) return x; return internal::min(x, y); } template , int> = 0> LIBC_INLINE T fmax(T x, T y) { FPBits bitx(x), bity(y); if (bitx.is_nan()) return y; if (bity.is_nan()) return x; return internal::max(x, y); } template , int> = 0> LIBC_INLINE T fmaximum(T x, T y) { FPBits bitx(x), bity(y); if (bitx.is_nan()) return x; if (bity.is_nan()) return y; return internal::max(x, y); } template , int> = 0> LIBC_INLINE T fminimum(T x, T y) { const FPBits bitx(x), bity(y); if (bitx.is_nan()) return x; if (bity.is_nan()) return y; return internal::min(x, y); } template , int> = 0> LIBC_INLINE T fmaximum_num(T x, T y) { FPBits bitx(x), bity(y); if (bitx.is_signaling_nan() || bity.is_signaling_nan()) { fputil::raise_except_if_required(FE_INVALID); if (bitx.is_nan() && bity.is_nan()) return FPBits::quiet_nan().get_val(); } if (bitx.is_nan()) return y; if (bity.is_nan()) return x; return internal::max(x, y); } template , int> = 0> LIBC_INLINE T fminimum_num(T x, T y) { FPBits bitx(x), bity(y); if (bitx.is_signaling_nan() || bity.is_signaling_nan()) { fputil::raise_except_if_required(FE_INVALID); if (bitx.is_nan() && bity.is_nan()) return FPBits::quiet_nan().get_val(); } if (bitx.is_nan()) return y; if (bity.is_nan()) return x; return internal::min(x, y); } template , int> = 0> LIBC_INLINE T fmaximum_mag(T x, T y) { FPBits bitx(x), bity(y); if (abs(x) > abs(y)) return x; if (abs(y) > abs(x)) return y; return fmaximum(x, y); } template , int> = 0> LIBC_INLINE T fminimum_mag(T x, T y) { FPBits bitx(x), bity(y); if (abs(x) < abs(y)) return x; if (abs(y) < abs(x)) return y; return fminimum(x, y); } template , int> = 0> LIBC_INLINE T fmaximum_mag_num(T x, T y) { FPBits bitx(x), bity(y); if (abs(x) > abs(y)) return x; if (abs(y) > abs(x)) return y; return fmaximum_num(x, y); } template , int> = 0> LIBC_INLINE T fminimum_mag_num(T x, T y) { FPBits bitx(x), bity(y); if (abs(x) < abs(y)) return x; if (abs(y) < abs(x)) return y; return fminimum_num(x, y); } template , int> = 0> LIBC_INLINE T fdim(T x, T y) { FPBits bitx(x), bity(y); if (bitx.is_nan()) { return x; } if (bity.is_nan()) { return y; } return (x > y ? x - y : 0); } // Avoid reusing `issignaling` macro. template , int> = 0> LIBC_INLINE int issignaling_impl(const T &x) { FPBits sx(x); return sx.is_signaling_nan(); } template , int> = 0> LIBC_INLINE int canonicalize(T &cx, const T &x) { FPBits sx(x); if constexpr (get_fp_type() == FPType::X86_Binary80) { // All the pseudo and unnormal numbers are not canonical. // More precisely : // Exponent | Significand | Meaning // | Bits 63-62 | Bits 61-0 | // All Ones | 00 | Zero | Pseudo Infinity, Value = SNaN // All Ones | 00 | Non-Zero | Pseudo NaN, Value = SNaN // All Ones | 01 | Anything | Pseudo NaN, Value = SNaN // | Bit 63 | Bits 62-0 | // All zeroes | One | Anything | Pseudo Denormal, Value = // | | | (−1)**s × m × 2**−16382 // All Other | Zero | Anything | Unnormal, Value = SNaN // Values | | | bool bit63 = sx.get_implicit_bit(); UInt128 mantissa = sx.get_explicit_mantissa(); bool bit62 = static_cast((mantissa & (1ULL << 62)) >> 62); int exponent = sx.get_biased_exponent(); if (exponent == 0x7FFF) { if (!bit63 && !bit62) { if (mantissa == 0) { cx = FPBits::quiet_nan(sx.sign(), mantissa).get_val(); raise_except_if_required(FE_INVALID); return 1; } cx = FPBits::quiet_nan(sx.sign(), mantissa).get_val(); raise_except_if_required(FE_INVALID); return 1; } else if (!bit63 && bit62) { cx = FPBits::quiet_nan(sx.sign(), mantissa).get_val(); raise_except_if_required(FE_INVALID); return 1; } else if (LIBC_UNLIKELY(sx.is_signaling_nan())) { cx = FPBits::quiet_nan(sx.sign(), sx.get_explicit_mantissa()) .get_val(); raise_except_if_required(FE_INVALID); return 1; } else cx = x; } else if (exponent == 0 && bit63) cx = FPBits::make_value(mantissa, 0).get_val(); else if (exponent != 0 && !bit63) { cx = FPBits::quiet_nan(sx.sign(), mantissa).get_val(); raise_except_if_required(FE_INVALID); return 1; } else if (LIBC_UNLIKELY(sx.is_signaling_nan())) { cx = FPBits::quiet_nan(sx.sign(), sx.get_explicit_mantissa()).get_val(); raise_except_if_required(FE_INVALID); return 1; } else cx = x; } else if (LIBC_UNLIKELY(sx.is_signaling_nan())) { cx = FPBits::quiet_nan(sx.sign(), sx.get_explicit_mantissa()).get_val(); raise_except_if_required(FE_INVALID); return 1; } else cx = x; return 0; } template LIBC_INLINE cpp::enable_if_t, bool> totalorder(T x, T y) { using FPBits = FPBits; FPBits x_bits(x); FPBits y_bits(y); using StorageType = typename FPBits::StorageType; StorageType x_u = x_bits.uintval(); StorageType y_u = y_bits.uintval(); bool has_neg = ((x_u | y_u) & FPBits::SIGN_MASK) != 0; return x_u == y_u || ((x_u < y_u) != has_neg); } template LIBC_INLINE cpp::enable_if_t, bool> totalordermag(T x, T y) { return FPBits(x).abs().uintval() <= FPBits(y).abs().uintval(); } template LIBC_INLINE cpp::enable_if_t, T> getpayload(T x) { using FPBits = FPBits; using StorageType = typename FPBits::StorageType; FPBits x_bits(x); if (!x_bits.is_nan()) return T(-1.0); StorageType payload = x_bits.uintval() & (FPBits::FRACTION_MASK >> 1); if constexpr (is_big_int_v) { DyadicFloat payload_dfloat(Sign::POS, 0, payload); return static_cast(payload_dfloat); } else { return static_cast(payload); } } template LIBC_INLINE cpp::enable_if_t, bool> setpayload(T &res, T pl) { using FPBits = FPBits; FPBits pl_bits(pl); // Signaling NaNs don't have the mantissa's MSB set to 1, so they need a // non-zero payload to distinguish them from infinities. if (!IsSignaling && pl_bits.is_zero()) { res = FPBits::quiet_nan(Sign::POS).get_val(); return false; } int pl_exp = pl_bits.get_exponent(); if (pl_bits.is_neg() || pl_exp < 0 || pl_exp >= FPBits::FRACTION_LEN - 1 || ((pl_bits.get_mantissa() << pl_exp) & FPBits::FRACTION_MASK) != 0) { res = T(0.0); return true; } using StorageType = typename FPBits::StorageType; StorageType v(pl_bits.get_explicit_mantissa() >> (FPBits::FRACTION_LEN - pl_exp)); if constexpr (IsSignaling) res = FPBits::signaling_nan(Sign::POS, v).get_val(); else res = FPBits::quiet_nan(Sign::POS, v).get_val(); return false; } } // namespace fputil } // namespace LIBC_NAMESPACE_DECL #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H