Path: blob/main/contrib/llvm-project/libc/src/__support/FPUtil/BasicOperations.h
213799 views
//===-- Basic operations on floating point numbers --------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H9#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H1011#include "FEnvImpl.h"12#include "FPBits.h"13#include "dyadic_float.h"1415#include "src/__support/CPP/type_traits.h"16#include "src/__support/big_int.h"17#include "src/__support/common.h"18#include "src/__support/macros/config.h"19#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY20#include "src/__support/macros/properties/architectures.h"21#include "src/__support/macros/properties/types.h"22#include "src/__support/uint128.h"2324namespace LIBC_NAMESPACE_DECL {25namespace fputil {2627template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>28LIBC_INLINE T abs(T x) {29return FPBits<T>(x).abs().get_val();30}3132namespace internal {3334template <typename T>35LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> max(T x, T y) {36FPBits<T> x_bits(x);37FPBits<T> y_bits(y);3839// To make sure that fmax(+0, -0) == +0 == fmax(-0, +0), whenever x and y40// have different signs and both are not NaNs, we return the number with41// positive sign.42if (x_bits.sign() != y_bits.sign())43return x_bits.is_pos() ? x : y;44return x > y ? x : y;45}4647#ifdef LIBC_TYPES_HAS_FLOAT1648#if defined(__LIBC_USE_BUILTIN_FMAXF16_FMINF16)49template <> LIBC_INLINE float16 max(float16 x, float16 y) {50return __builtin_fmaxf16(x, y);51}52#elif !defined(LIBC_TARGET_ARCH_IS_AARCH64)53template <> LIBC_INLINE float16 max(float16 x, float16 y) {54FPBits<float16> x_bits(x);55FPBits<float16> y_bits(y);5657int16_t xi = static_cast<int16_t>(x_bits.uintval());58int16_t yi = static_cast<int16_t>(y_bits.uintval());59return ((xi > yi) != (xi < 0 && yi < 0)) ? x : y;60}61#endif62#endif // LIBC_TYPES_HAS_FLOAT166364#if defined(__LIBC_USE_BUILTIN_FMAX_FMIN) && !defined(LIBC_TARGET_ARCH_IS_X86)65template <> LIBC_INLINE float max(float x, float y) {66return __builtin_fmaxf(x, y);67}6869template <> LIBC_INLINE double max(double x, double y) {70return __builtin_fmax(x, y);71}72#endif7374template <typename T>75LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> min(T x, T y) {76FPBits<T> x_bits(x);77FPBits<T> y_bits(y);7879// To make sure that fmin(+0, -0) == -0 == fmin(-0, +0), whenever x and y have80// different signs and both are not NaNs, we return the number with negative81// sign.82if (x_bits.sign() != y_bits.sign())83return x_bits.is_neg() ? x : y;84return x < y ? x : y;85}8687#ifdef LIBC_TYPES_HAS_FLOAT1688#if defined(__LIBC_USE_BUILTIN_FMAXF16_FMINF16)89template <> LIBC_INLINE float16 min(float16 x, float16 y) {90return __builtin_fminf16(x, y);91}92#elif !defined(LIBC_TARGET_ARCH_IS_AARCH64)93template <> LIBC_INLINE float16 min(float16 x, float16 y) {94FPBits<float16> x_bits(x);95FPBits<float16> y_bits(y);9697int16_t xi = static_cast<int16_t>(x_bits.uintval());98int16_t yi = static_cast<int16_t>(y_bits.uintval());99return ((xi < yi) != (xi < 0 && yi < 0)) ? x : y;100}101#endif102#endif // LIBC_TYPES_HAS_FLOAT16103104#if defined(__LIBC_USE_BUILTIN_FMAX_FMIN) && !defined(LIBC_TARGET_ARCH_IS_X86)105template <> LIBC_INLINE float min(float x, float y) {106return __builtin_fminf(x, y);107}108109template <> LIBC_INLINE double min(double x, double y) {110return __builtin_fmin(x, y);111}112#endif113114} // namespace internal115116template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>117LIBC_INLINE T fmin(T x, T y) {118const FPBits<T> bitx(x), bity(y);119120if (bitx.is_nan())121return y;122if (bity.is_nan())123return x;124return internal::min(x, y);125}126127template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>128LIBC_INLINE T fmax(T x, T y) {129FPBits<T> bitx(x), bity(y);130131if (bitx.is_nan())132return y;133if (bity.is_nan())134return x;135return internal::max(x, y);136}137138template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>139LIBC_INLINE T fmaximum(T x, T y) {140FPBits<T> bitx(x), bity(y);141142if (bitx.is_nan())143return x;144if (bity.is_nan())145return y;146return internal::max(x, y);147}148149template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>150LIBC_INLINE T fminimum(T x, T y) {151const FPBits<T> bitx(x), bity(y);152153if (bitx.is_nan())154return x;155if (bity.is_nan())156return y;157return internal::min(x, y);158}159160template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>161LIBC_INLINE T fmaximum_num(T x, T y) {162FPBits<T> bitx(x), bity(y);163if (bitx.is_signaling_nan() || bity.is_signaling_nan()) {164fputil::raise_except_if_required(FE_INVALID);165if (bitx.is_nan() && bity.is_nan())166return FPBits<T>::quiet_nan().get_val();167}168if (bitx.is_nan())169return y;170if (bity.is_nan())171return x;172return internal::max(x, y);173}174175template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>176LIBC_INLINE T fminimum_num(T x, T y) {177FPBits<T> bitx(x), bity(y);178if (bitx.is_signaling_nan() || bity.is_signaling_nan()) {179fputil::raise_except_if_required(FE_INVALID);180if (bitx.is_nan() && bity.is_nan())181return FPBits<T>::quiet_nan().get_val();182}183if (bitx.is_nan())184return y;185if (bity.is_nan())186return x;187return internal::min(x, y);188}189190template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>191LIBC_INLINE T fmaximum_mag(T x, T y) {192FPBits<T> bitx(x), bity(y);193194if (abs(x) > abs(y))195return x;196if (abs(y) > abs(x))197return y;198return fmaximum(x, y);199}200201template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>202LIBC_INLINE T fminimum_mag(T x, T y) {203FPBits<T> bitx(x), bity(y);204205if (abs(x) < abs(y))206return x;207if (abs(y) < abs(x))208return y;209return fminimum(x, y);210}211212template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>213LIBC_INLINE T fmaximum_mag_num(T x, T y) {214FPBits<T> bitx(x), bity(y);215216if (abs(x) > abs(y))217return x;218if (abs(y) > abs(x))219return y;220return fmaximum_num(x, y);221}222223template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>224LIBC_INLINE T fminimum_mag_num(T x, T y) {225FPBits<T> bitx(x), bity(y);226227if (abs(x) < abs(y))228return x;229if (abs(y) < abs(x))230return y;231return fminimum_num(x, y);232}233234template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>235LIBC_INLINE T fdim(T x, T y) {236FPBits<T> bitx(x), bity(y);237238if (bitx.is_nan()) {239return x;240}241242if (bity.is_nan()) {243return y;244}245246return (x > y ? x - y : 0);247}248249// Avoid reusing `issignaling` macro.250template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>251LIBC_INLINE int issignaling_impl(const T &x) {252FPBits<T> sx(x);253return sx.is_signaling_nan();254}255256template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>257LIBC_INLINE int canonicalize(T &cx, const T &x) {258FPBits<T> sx(x);259if constexpr (get_fp_type<T>() == FPType::X86_Binary80) {260// All the pseudo and unnormal numbers are not canonical.261// More precisely :262// Exponent | Significand | Meaning263// | Bits 63-62 | Bits 61-0 |264// All Ones | 00 | Zero | Pseudo Infinity, Value = SNaN265// All Ones | 00 | Non-Zero | Pseudo NaN, Value = SNaN266// All Ones | 01 | Anything | Pseudo NaN, Value = SNaN267// | Bit 63 | Bits 62-0 |268// All zeroes | One | Anything | Pseudo Denormal, Value =269// | | | (−1)**s × m × 2**−16382270// All Other | Zero | Anything | Unnormal, Value = SNaN271// Values | | |272bool bit63 = sx.get_implicit_bit();273UInt128 mantissa = sx.get_explicit_mantissa();274bool bit62 = static_cast<bool>((mantissa & (1ULL << 62)) >> 62);275int exponent = sx.get_biased_exponent();276if (exponent == 0x7FFF) {277if (!bit63 && !bit62) {278if (mantissa == 0) {279cx = FPBits<T>::quiet_nan(sx.sign(), mantissa).get_val();280raise_except_if_required(FE_INVALID);281return 1;282}283cx = FPBits<T>::quiet_nan(sx.sign(), mantissa).get_val();284raise_except_if_required(FE_INVALID);285return 1;286} else if (!bit63 && bit62) {287cx = FPBits<T>::quiet_nan(sx.sign(), mantissa).get_val();288raise_except_if_required(FE_INVALID);289return 1;290} else if (LIBC_UNLIKELY(sx.is_signaling_nan())) {291cx = FPBits<T>::quiet_nan(sx.sign(), sx.get_explicit_mantissa())292.get_val();293raise_except_if_required(FE_INVALID);294return 1;295} else296cx = x;297} else if (exponent == 0 && bit63)298cx = FPBits<T>::make_value(mantissa, 0).get_val();299else if (exponent != 0 && !bit63) {300cx = FPBits<T>::quiet_nan(sx.sign(), mantissa).get_val();301raise_except_if_required(FE_INVALID);302return 1;303} else if (LIBC_UNLIKELY(sx.is_signaling_nan())) {304cx =305FPBits<T>::quiet_nan(sx.sign(), sx.get_explicit_mantissa()).get_val();306raise_except_if_required(FE_INVALID);307return 1;308} else309cx = x;310} else if (LIBC_UNLIKELY(sx.is_signaling_nan())) {311cx = FPBits<T>::quiet_nan(sx.sign(), sx.get_explicit_mantissa()).get_val();312raise_except_if_required(FE_INVALID);313return 1;314} else315cx = x;316return 0;317}318319template <typename T>320LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>321totalorder(T x, T y) {322using FPBits = FPBits<T>;323FPBits x_bits(x);324FPBits y_bits(y);325326using StorageType = typename FPBits::StorageType;327StorageType x_u = x_bits.uintval();328StorageType y_u = y_bits.uintval();329330bool has_neg = ((x_u | y_u) & FPBits::SIGN_MASK) != 0;331return x_u == y_u || ((x_u < y_u) != has_neg);332}333334template <typename T>335LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>336totalordermag(T x, T y) {337return FPBits<T>(x).abs().uintval() <= FPBits<T>(y).abs().uintval();338}339340template <typename T>341LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> getpayload(T x) {342using FPBits = FPBits<T>;343using StorageType = typename FPBits::StorageType;344FPBits x_bits(x);345346if (!x_bits.is_nan())347return T(-1.0);348349StorageType payload = x_bits.uintval() & (FPBits::FRACTION_MASK >> 1);350351if constexpr (is_big_int_v<StorageType>) {352DyadicFloat<FPBits::STORAGE_LEN> payload_dfloat(Sign::POS, 0, payload);353354return static_cast<T>(payload_dfloat);355} else {356return static_cast<T>(payload);357}358}359360template <bool IsSignaling, typename T>361LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>362setpayload(T &res, T pl) {363using FPBits = FPBits<T>;364FPBits pl_bits(pl);365366// Signaling NaNs don't have the mantissa's MSB set to 1, so they need a367// non-zero payload to distinguish them from infinities.368if (!IsSignaling && pl_bits.is_zero()) {369res = FPBits::quiet_nan(Sign::POS).get_val();370return false;371}372373int pl_exp = pl_bits.get_exponent();374375if (pl_bits.is_neg() || pl_exp < 0 || pl_exp >= FPBits::FRACTION_LEN - 1 ||376((pl_bits.get_mantissa() << pl_exp) & FPBits::FRACTION_MASK) != 0) {377res = T(0.0);378return true;379}380381using StorageType = typename FPBits::StorageType;382StorageType v(pl_bits.get_explicit_mantissa() >>383(FPBits::FRACTION_LEN - pl_exp));384385if constexpr (IsSignaling)386res = FPBits::signaling_nan(Sign::POS, v).get_val();387else388res = FPBits::quiet_nan(Sign::POS, v).get_val();389return false;390}391392} // namespace fputil393} // namespace LIBC_NAMESPACE_DECL394395#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H396397398