xref: /aosp_15_r20/external/llvm-libc/src/__support/FPUtil/generic/add_sub.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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