Path: blob/main/contrib/llvm-project/libc/src/__support/FPUtil/NearestIntegerOperations.h
213799 views
//===-- Nearest integer floating-point operations ---------------*- 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_NEARESTINTEGEROPERATIONS_H9#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_NEARESTINTEGEROPERATIONS_H1011#include "FEnvImpl.h"12#include "FPBits.h"13#include "rounding_mode.h"1415#include "hdr/math_macros.h"16#include "src/__support/CPP/type_traits.h"17#include "src/__support/common.h"18#include "src/__support/macros/config.h"1920namespace LIBC_NAMESPACE_DECL {21namespace fputil {2223template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>24LIBC_INLINE T trunc(T x) {25using StorageType = typename FPBits<T>::StorageType;26FPBits<T> bits(x);2728// If x is infinity or NaN, return it.29// If it is zero also we should return it as is, but the logic30// later in this function takes care of it. But not doing a zero31// check, we improve the run time of non-zero values.32if (bits.is_inf_or_nan())33return x;3435int exponent = bits.get_exponent();3637// If the exponent is greater than the most negative mantissa38// exponent, then x is already an integer.39if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN))40return x;4142// If the exponent is such that abs(x) is less than 1, then return 0.43if (exponent <= -1)44return FPBits<T>::zero(bits.sign()).get_val();4546int trim_size = FPBits<T>::FRACTION_LEN - exponent;47StorageType trunc_mantissa =48static_cast<StorageType>((bits.get_mantissa() >> trim_size) << trim_size);49bits.set_mantissa(trunc_mantissa);50return bits.get_val();51}5253template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>54LIBC_INLINE T ceil(T x) {55using StorageType = typename FPBits<T>::StorageType;56FPBits<T> bits(x);5758// If x is infinity NaN or zero, return it.59if (bits.is_inf_or_nan() || bits.is_zero())60return x;6162bool is_neg = bits.is_neg();63int exponent = bits.get_exponent();6465// If the exponent is greater than the most negative mantissa66// exponent, then x is already an integer.67if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN))68return x;6970if (exponent <= -1) {71if (is_neg)72return T(-0.0);73else74return T(1.0);75}7677uint32_t trim_size = FPBits<T>::FRACTION_LEN - exponent;78StorageType x_u = bits.uintval();79StorageType trunc_u =80static_cast<StorageType>((x_u >> trim_size) << trim_size);8182// If x is already an integer, return it.83if (trunc_u == x_u)84return x;8586bits.set_uintval(trunc_u);87T trunc_value = bits.get_val();8889// If x is negative, the ceil operation is equivalent to the trunc operation.90if (is_neg)91return trunc_value;9293return trunc_value + T(1.0);94}9596template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>97LIBC_INLINE T floor(T x) {98FPBits<T> bits(x);99if (bits.is_neg()) {100return -ceil(-x);101} else {102return trunc(x);103}104}105106template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>107LIBC_INLINE T round(T x) {108using StorageType = typename FPBits<T>::StorageType;109FPBits<T> bits(x);110111// If x is infinity NaN or zero, return it.112if (bits.is_inf_or_nan() || bits.is_zero())113return x;114115int exponent = bits.get_exponent();116117// If the exponent is greater than the most negative mantissa118// exponent, then x is already an integer.119if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN))120return x;121122if (exponent == -1) {123// Absolute value of x is greater than equal to 0.5 but less than 1.124return FPBits<T>::one(bits.sign()).get_val();125}126127if (exponent <= -2) {128// Absolute value of x is less than 0.5.129return FPBits<T>::zero(bits.sign()).get_val();130}131132uint32_t trim_size = FPBits<T>::FRACTION_LEN - exponent;133bool half_bit_set =134bool(bits.get_mantissa() & (StorageType(1) << (trim_size - 1)));135StorageType x_u = bits.uintval();136StorageType trunc_u =137static_cast<StorageType>((x_u >> trim_size) << trim_size);138139// If x is already an integer, return it.140if (trunc_u == x_u)141return x;142143bits.set_uintval(trunc_u);144T trunc_value = bits.get_val();145146if (!half_bit_set) {147// Franctional part is less than 0.5 so round value is the148// same as the trunc value.149return trunc_value;150} else {151return bits.is_neg() ? trunc_value - T(1.0) : trunc_value + T(1.0);152}153}154155template <typename T>156LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T>157round_using_specific_rounding_mode(T x, int rnd) {158using StorageType = typename FPBits<T>::StorageType;159FPBits<T> bits(x);160161// If x is infinity NaN or zero, return it.162if (bits.is_inf_or_nan() || bits.is_zero())163return x;164165bool is_neg = bits.is_neg();166int exponent = bits.get_exponent();167168// If the exponent is greater than the most negative mantissa169// exponent, then x is already an integer.170if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN))171return x;172173if (exponent <= -1) {174switch (rnd) {175case FP_INT_DOWNWARD:176return is_neg ? T(-1.0) : T(0.0);177case FP_INT_UPWARD:178return is_neg ? T(-0.0) : T(1.0);179case FP_INT_TOWARDZERO:180return is_neg ? T(-0.0) : T(0.0);181case FP_INT_TONEARESTFROMZERO:182if (exponent < -1)183return is_neg ? T(-0.0) : T(0.0); // abs(x) < 0.5184return is_neg ? T(-1.0) : T(1.0); // abs(x) >= 0.5185case FP_INT_TONEAREST:186default:187if (exponent <= -2 || bits.get_mantissa() == 0)188return is_neg ? T(-0.0) : T(0.0); // abs(x) <= 0.5189else190return is_neg ? T(-1.0) : T(1.0); // abs(x) > 0.5191}192}193194uint32_t trim_size = FPBits<T>::FRACTION_LEN - exponent;195StorageType x_u = bits.uintval();196StorageType trunc_u =197static_cast<StorageType>((x_u >> trim_size) << trim_size);198199// If x is already an integer, return it.200if (trunc_u == x_u)201return x;202203FPBits<T> new_bits(trunc_u);204T trunc_value = new_bits.get_val();205206StorageType trim_value =207bits.get_mantissa() &208static_cast<StorageType>(((StorageType(1) << trim_size) - 1));209StorageType half_value =210static_cast<StorageType>((StorageType(1) << (trim_size - 1)));211// If exponent is 0, trimSize will be equal to the mantissa width, and212// truncIsOdd` will not be correct. So, we handle it as a special case213// below.214StorageType trunc_is_odd =215new_bits.get_mantissa() & (StorageType(1) << trim_size);216217switch (rnd) {218case FP_INT_DOWNWARD:219return is_neg ? trunc_value - T(1.0) : trunc_value;220case FP_INT_UPWARD:221return is_neg ? trunc_value : trunc_value + T(1.0);222case FP_INT_TOWARDZERO:223return trunc_value;224case FP_INT_TONEARESTFROMZERO:225if (trim_value >= half_value)226return is_neg ? trunc_value - T(1.0) : trunc_value + T(1.0);227return trunc_value;228case FP_INT_TONEAREST:229default:230if (trim_value > half_value) {231return is_neg ? trunc_value - T(1.0) : trunc_value + T(1.0);232} else if (trim_value == half_value) {233if (exponent == 0)234return is_neg ? T(-2.0) : T(2.0);235if (trunc_is_odd)236return is_neg ? trunc_value - T(1.0) : trunc_value + T(1.0);237else238return trunc_value;239} else {240return trunc_value;241}242}243}244245template <typename T>246LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T>247round_using_current_rounding_mode(T x) {248int rounding_mode = quick_get_round();249250switch (rounding_mode) {251case FE_DOWNWARD:252return round_using_specific_rounding_mode(x, FP_INT_DOWNWARD);253case FE_UPWARD:254return round_using_specific_rounding_mode(x, FP_INT_UPWARD);255case FE_TOWARDZERO:256return round_using_specific_rounding_mode(x, FP_INT_TOWARDZERO);257case FE_TONEAREST:258return round_using_specific_rounding_mode(x, FP_INT_TONEAREST);259default:260__builtin_unreachable();261}262}263264template <bool IsSigned, typename T>265LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T>266fromfp(T x, int rnd, unsigned int width) {267using StorageType = typename FPBits<T>::StorageType;268269constexpr StorageType EXPLICIT_BIT =270FPBits<T>::SIG_MASK - FPBits<T>::FRACTION_MASK;271272if (width == 0U) {273raise_except_if_required(FE_INVALID);274return FPBits<T>::quiet_nan().get_val();275}276277FPBits<T> bits(x);278279if (bits.is_inf_or_nan()) {280raise_except_if_required(FE_INVALID);281return FPBits<T>::quiet_nan().get_val();282}283284T rounded_value = round_using_specific_rounding_mode(x, rnd);285286if constexpr (IsSigned) {287// T can't hold a finite number >= 2.0 * 2^EXP_BIAS.288if (width - 1 > FPBits<T>::EXP_BIAS)289return rounded_value;290291StorageType range_exp =292static_cast<StorageType>(width - 1 + FPBits<T>::EXP_BIAS);293// rounded_value < -2^(width - 1)294T range_min =295FPBits<T>::create_value(Sign::NEG, range_exp, EXPLICIT_BIT).get_val();296if (rounded_value < range_min) {297raise_except_if_required(FE_INVALID);298return FPBits<T>::quiet_nan().get_val();299}300// rounded_value > 2^(width - 1) - 1301T range_max =302FPBits<T>::create_value(Sign::POS, range_exp, EXPLICIT_BIT).get_val() -303T(1.0);304if (rounded_value > range_max) {305raise_except_if_required(FE_INVALID);306return FPBits<T>::quiet_nan().get_val();307}308309return rounded_value;310}311312if (rounded_value < T(0.0)) {313raise_except_if_required(FE_INVALID);314return FPBits<T>::quiet_nan().get_val();315}316317// T can't hold a finite number >= 2.0 * 2^EXP_BIAS.318if (width > FPBits<T>::EXP_BIAS)319return rounded_value;320321StorageType range_exp = static_cast<StorageType>(width + FPBits<T>::EXP_BIAS);322// rounded_value > 2^width - 1323T range_max =324FPBits<T>::create_value(Sign::POS, range_exp, EXPLICIT_BIT).get_val() -325T(1.0);326if (rounded_value > range_max) {327raise_except_if_required(FE_INVALID);328return FPBits<T>::quiet_nan().get_val();329}330331return rounded_value;332}333334template <bool IsSigned, typename T>335LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T>336fromfpx(T x, int rnd, unsigned int width) {337T rounded_value = fromfp<IsSigned>(x, rnd, width);338FPBits<T> bits(rounded_value);339340if (!bits.is_nan() && rounded_value != x)341raise_except_if_required(FE_INEXACT);342343return rounded_value;344}345346namespace internal {347348template <typename FloatType, typename IntType,349cpp::enable_if_t<cpp::is_floating_point_v<FloatType> &&350cpp::is_integral_v<IntType>,351int> = 0>352LIBC_INLINE IntType rounded_float_to_signed_integer(FloatType x) {353constexpr IntType INTEGER_MIN = (IntType(1) << (sizeof(IntType) * 8 - 1));354constexpr IntType INTEGER_MAX = -(INTEGER_MIN + 1);355FPBits<FloatType> bits(x);356auto set_domain_error_and_raise_invalid = []() {357set_errno_if_required(EDOM);358raise_except_if_required(FE_INVALID);359};360361if (bits.is_inf_or_nan()) {362set_domain_error_and_raise_invalid();363return bits.is_neg() ? INTEGER_MIN : INTEGER_MAX;364}365366int exponent = bits.get_exponent();367constexpr int EXPONENT_LIMIT = sizeof(IntType) * 8 - 1;368if (exponent > EXPONENT_LIMIT) {369set_domain_error_and_raise_invalid();370return bits.is_neg() ? INTEGER_MIN : INTEGER_MAX;371} else if (exponent == EXPONENT_LIMIT) {372if (bits.is_pos() || bits.get_mantissa() != 0) {373set_domain_error_and_raise_invalid();374return bits.is_neg() ? INTEGER_MIN : INTEGER_MAX;375}376// If the control reaches here, then it means that the rounded377// value is the most negative number for the signed integer type IntType.378}379380// For all other cases, if `x` can fit in the integer type `IntType`,381// we just return `x`. static_cast will convert the floating382// point value to the exact integer value.383return static_cast<IntType>(x);384}385386} // namespace internal387388template <typename FloatType, typename IntType,389cpp::enable_if_t<cpp::is_floating_point_v<FloatType> &&390cpp::is_integral_v<IntType>,391int> = 0>392LIBC_INLINE IntType round_to_signed_integer(FloatType x) {393return internal::rounded_float_to_signed_integer<FloatType, IntType>(394round(x));395}396397template <typename FloatType, typename IntType,398cpp::enable_if_t<cpp::is_floating_point_v<FloatType> &&399cpp::is_integral_v<IntType>,400int> = 0>401LIBC_INLINE IntType402round_to_signed_integer_using_current_rounding_mode(FloatType x) {403return internal::rounded_float_to_signed_integer<FloatType, IntType>(404round_using_current_rounding_mode(x));405}406407} // namespace fputil408} // namespace LIBC_NAMESPACE_DECL409410#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_NEARESTINTEGEROPERATIONS_H411412413