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>
add_or_sub(InType x,InType y)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>
add(InType x,InType y)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>
sub(InType x,InType y)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