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