//===-- 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 "FEnvImpl.h" #include "src/__support/CPP/type_traits.h" #include "src/__support/common.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY #include "src/__support/uint128.h" namespace LIBC_NAMESPACE { namespace fputil { template , int> = 0> LIBC_INLINE T abs(T x) { return FPBits(x).abs().get_val(); } 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; if (bitx.sign() != bity.sign()) // To make sure that fmin(+0, -0) == -0 == fmin(-0, +0), whenever x and // y has different signs and both are not NaNs, we return the number // with negative sign. return bitx.is_neg() ? x : y; return x < y ? 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; if (bitx.sign() != bity.sign()) // To make sure that fmax(+0, -0) == +0 == fmax(-0, +0), whenever x and // y has different signs and both are not NaNs, we return the number // with positive sign. return bitx.is_neg() ? y : x; return x > y ? 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; if (bitx.sign() != bity.sign()) return (bitx.is_neg() ? y : x); return x > y ? 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; if (bitx.sign() != bity.sign()) return (bitx.is_neg()) ? x : y; return x < y ? 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; if (bitx.sign() != bity.sign()) return (bitx.is_neg() ? y : x); return x > y ? 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; if (bitx.sign() != bity.sign()) return (bitx.is_neg() ? x : y); return x < y ? 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); } 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; } } // namespace fputil } // namespace LIBC_NAMESPACE #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H