226 lines · c
1//===-- Add and subtract IEEE 754 floating-point numbers --------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H10#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H11 12#include "hdr/fenv_macros.h"13#include "src/__support/CPP/algorithm.h"14#include "src/__support/CPP/bit.h"15#include "src/__support/CPP/type_traits.h"16#include "src/__support/FPUtil/BasicOperations.h"17#include "src/__support/FPUtil/FEnvImpl.h"18#include "src/__support/FPUtil/FPBits.h"19#include "src/__support/FPUtil/cast.h"20#include "src/__support/FPUtil/dyadic_float.h"21#include "src/__support/FPUtil/rounding_mode.h"22#include "src/__support/macros/attributes.h"23#include "src/__support/macros/config.h"24#include "src/__support/macros/optimization.h"25 26namespace LIBC_NAMESPACE_DECL {27namespace fputil::generic {28 29template <bool IsSub, typename OutType, typename InType>30LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&31 cpp::is_floating_point_v<InType> &&32 sizeof(OutType) <= sizeof(InType),33 OutType>34add_or_sub(InType x, InType y) {35 using OutFPBits = FPBits<OutType>;36 using OutStorageType = typename OutFPBits::StorageType;37 using InFPBits = FPBits<InType>;38 using InStorageType = typename InFPBits::StorageType;39 40 constexpr int GUARD_BITS_LEN = 3;41 constexpr int RESULT_FRACTION_LEN = InFPBits::FRACTION_LEN + GUARD_BITS_LEN;42 constexpr int RESULT_MANTISSA_LEN = RESULT_FRACTION_LEN + 1;43 44 using DyadicFloat =45 DyadicFloat<cpp::bit_ceil(static_cast<size_t>(RESULT_MANTISSA_LEN))>;46 47 InFPBits x_bits(x);48 InFPBits y_bits(y);49 50 bool is_effectively_add = (x_bits.sign() == y_bits.sign()) != IsSub;51 52 if (LIBC_UNLIKELY(x_bits.is_inf_or_nan() || y_bits.is_inf_or_nan() ||53 x_bits.is_zero() || y_bits.is_zero())) {54 if (x_bits.is_nan() || y_bits.is_nan()) {55 if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())56 raise_except_if_required(FE_INVALID);57 58 if (x_bits.is_quiet_nan()) {59 InStorageType x_payload = x_bits.get_mantissa();60 x_payload >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;61 return OutFPBits::quiet_nan(x_bits.sign(),62 static_cast<OutStorageType>(x_payload))63 .get_val();64 }65 66 if (y_bits.is_quiet_nan()) {67 InStorageType y_payload = y_bits.get_mantissa();68 y_payload >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;69 return OutFPBits::quiet_nan(y_bits.sign(),70 static_cast<OutStorageType>(y_payload))71 .get_val();72 }73 74 return OutFPBits::quiet_nan().get_val();75 }76 77 if (x_bits.is_inf()) {78 if (y_bits.is_inf()) {79 if (!is_effectively_add) {80 raise_except_if_required(FE_INVALID);81 return OutFPBits::quiet_nan().get_val();82 }83 84 return OutFPBits::inf(x_bits.sign()).get_val();85 }86 87 return OutFPBits::inf(x_bits.sign()).get_val();88 }89 90 if (y_bits.is_inf()) {91 if constexpr (IsSub)92 return OutFPBits::inf(y_bits.sign().negate()).get_val();93 else94 return OutFPBits::inf(y_bits.sign()).get_val();95 }96 97 if (x_bits.is_zero()) {98 if (y_bits.is_zero()) {99 switch (quick_get_round()) {100 case FE_DOWNWARD:101 return OutFPBits::zero(Sign::NEG).get_val();102 default:103 return OutFPBits::zero(Sign::POS).get_val();104 }105 }106 107 if constexpr (cpp::is_same_v<InType, bfloat16> &&108 cpp::is_same_v<OutType, bfloat16>) {109 OutFPBits y_bits(y);110 if constexpr (IsSub)111 y_bits.set_sign(y_bits.sign().negate());112 return y_bits.get_val();113 } else {114 115 // volatile prevents Clang from converting tmp to OutType and then116 // immediately back to InType before negating it, resulting in double117 // rounding.118 volatile InType tmp = y;119 if constexpr (IsSub)120 tmp = -tmp;121 return cast<OutType>(tmp);122 }123 }124 125 if (y_bits.is_zero())126 return cast<OutType>(x);127 }128 129 InType x_abs = x_bits.abs().get_val();130 InType y_abs = y_bits.abs().get_val();131 132 if (x_abs == y_abs && !is_effectively_add) {133 switch (quick_get_round()) {134 case FE_DOWNWARD:135 return OutFPBits::zero(Sign::NEG).get_val();136 default:137 return OutFPBits::zero(Sign::POS).get_val();138 }139 }140 141 Sign result_sign = Sign::POS;142 143 if (x_abs > y_abs) {144 result_sign = x_bits.sign();145 } else if (x_abs < y_abs) {146 if (is_effectively_add)147 result_sign = y_bits.sign();148 else if (y_bits.is_pos())149 result_sign = Sign::NEG;150 } else if (is_effectively_add) {151 result_sign = x_bits.sign();152 }153 154 InFPBits max_bits(cpp::max(x_abs, y_abs));155 InFPBits min_bits(cpp::min(x_abs, y_abs));156 157 InStorageType result_mant;158 159 if (max_bits.is_subnormal()) {160 // min_bits must be subnormal too.161 162 if (is_effectively_add)163 result_mant = max_bits.get_mantissa() + min_bits.get_mantissa();164 else165 result_mant = max_bits.get_mantissa() - min_bits.get_mantissa();166 167 result_mant <<= GUARD_BITS_LEN;168 } else {169 InStorageType max_mant = static_cast<InStorageType>(170 max_bits.get_explicit_mantissa() << GUARD_BITS_LEN);171 InStorageType min_mant = static_cast<InStorageType>(172 min_bits.get_explicit_mantissa() << GUARD_BITS_LEN);173 174 int alignment = (max_bits.get_biased_exponent() - max_bits.is_normal()) -175 (min_bits.get_biased_exponent() - min_bits.is_normal());176 177 InStorageType aligned_min_mant = static_cast<InStorageType>(178 min_mant >> cpp::min(alignment, RESULT_MANTISSA_LEN));179 bool aligned_min_mant_sticky;180 181 if (alignment <= GUARD_BITS_LEN)182 aligned_min_mant_sticky = false;183 else if (alignment > InFPBits::FRACTION_LEN + GUARD_BITS_LEN)184 aligned_min_mant_sticky = true;185 else186 aligned_min_mant_sticky =187 (static_cast<InStorageType>(188 min_mant << (InFPBits::STORAGE_LEN - alignment))) != 0;189 190 InStorageType min_mant_sticky =191 static_cast<InStorageType>(static_cast<int>(aligned_min_mant_sticky));192 193 if (is_effectively_add)194 result_mant = max_mant + (aligned_min_mant | min_mant_sticky);195 else196 result_mant = max_mant - (aligned_min_mant | min_mant_sticky);197 }198 199 int result_exp = max_bits.get_explicit_exponent() - RESULT_FRACTION_LEN;200 DyadicFloat result(result_sign, result_exp, result_mant);201 return result.template as<OutType, /*ShouldSignalExceptions=*/true>();202}203 204template <typename OutType, typename InType>205LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&206 cpp::is_floating_point_v<InType> &&207 sizeof(OutType) <= sizeof(InType),208 OutType>209add(InType x, InType y) {210 return add_or_sub</*IsSub=*/false, OutType>(x, y);211}212 213template <typename OutType, typename InType>214LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&215 cpp::is_floating_point_v<InType> &&216 sizeof(OutType) <= sizeof(InType),217 OutType>218sub(InType x, InType y) {219 return add_or_sub</*IsSub=*/true, OutType>(x, y);220}221 222} // namespace fputil::generic223} // namespace LIBC_NAMESPACE_DECL224 225#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_ADD_SUB_H226