1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // Copyright (C) 2021 Intel Corporation. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #ifndef QNUMERIC_P_H |
6 | #define QNUMERIC_P_H |
7 | |
8 | // |
9 | // W A R N I N G |
10 | // ------------- |
11 | // |
12 | // This file is not part of the Qt API. It exists purely as an |
13 | // implementation detail. This header file may change from version to |
14 | // version without notice, or even be removed. |
15 | // |
16 | // We mean it. |
17 | // |
18 | |
19 | #include "QtCore/private/qglobal_p.h" |
20 | #include "QtCore/qnumeric.h" |
21 | #include "QtCore/qsimd.h" |
22 | #include <cmath> |
23 | #include <limits> |
24 | #include <type_traits> |
25 | |
26 | #if !defined(Q_CC_MSVC) && defined(Q_OS_QNX) |
27 | # include <math.h> |
28 | # ifdef isnan |
29 | # define QT_MATH_H_DEFINES_MACROS |
30 | QT_BEGIN_NAMESPACE |
31 | namespace qnumeric_std_wrapper { |
32 | // the 'using namespace std' below is cases where the stdlib already put the math.h functions in the std namespace and undefined the macros. |
33 | Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(double d) { using namespace std; return isnan(d); } |
34 | Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(double d) { using namespace std; return isinf(d); } |
35 | Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(double d) { using namespace std; return isfinite(d); } |
36 | Q_DECL_CONST_FUNCTION static inline int math_h_fpclassify(double d) { using namespace std; return fpclassify(d); } |
37 | Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(float f) { using namespace std; return isnan(f); } |
38 | Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(float f) { using namespace std; return isinf(f); } |
39 | Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(float f) { using namespace std; return isfinite(f); } |
40 | Q_DECL_CONST_FUNCTION static inline int math_h_fpclassify(float f) { using namespace std; return fpclassify(f); } |
41 | } |
42 | QT_END_NAMESPACE |
43 | // These macros from math.h conflict with the real functions in the std namespace. |
44 | # undef signbit |
45 | # undef isnan |
46 | # undef isinf |
47 | # undef isfinite |
48 | # undef fpclassify |
49 | # endif // defined(isnan) |
50 | #endif |
51 | |
52 | QT_BEGIN_NAMESPACE |
53 | |
54 | namespace qnumeric_std_wrapper { |
55 | #if defined(QT_MATH_H_DEFINES_MACROS) |
56 | # undef QT_MATH_H_DEFINES_MACROS |
57 | Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return math_h_isnan(d); } |
58 | Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return math_h_isinf(d); } |
59 | Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return math_h_isfinite(d); } |
60 | Q_DECL_CONST_FUNCTION static inline int fpclassify(double d) { return math_h_fpclassify(d); } |
61 | Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return math_h_isnan(f); } |
62 | Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return math_h_isinf(f); } |
63 | Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return math_h_isfinite(f); } |
64 | Q_DECL_CONST_FUNCTION static inline int fpclassify(float f) { return math_h_fpclassify(f); } |
65 | #else |
66 | Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return std::isnan(x: d); } |
67 | Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return std::isinf(x: d); } |
68 | Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return std::isfinite(x: d); } |
69 | Q_DECL_CONST_FUNCTION static inline int fpclassify(double d) { return std::fpclassify(x: d); } |
70 | Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return std::isnan(x: f); } |
71 | Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return std::isinf(x: f); } |
72 | Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return std::isfinite(x: f); } |
73 | Q_DECL_CONST_FUNCTION static inline int fpclassify(float f) { return std::fpclassify(x: f); } |
74 | #endif |
75 | } |
76 | |
77 | constexpr Q_DECL_CONST_FUNCTION static inline double qt_inf() noexcept |
78 | { |
79 | static_assert(std::numeric_limits<double>::has_infinity, |
80 | "platform has no definition for infinity for type double" ); |
81 | return std::numeric_limits<double>::infinity(); |
82 | } |
83 | |
84 | #if QT_CONFIG(signaling_nan) |
85 | constexpr Q_DECL_CONST_FUNCTION static inline double qt_snan() noexcept |
86 | { |
87 | static_assert(std::numeric_limits<double>::has_signaling_NaN, |
88 | "platform has no definition for signaling NaN for type double" ); |
89 | return std::numeric_limits<double>::signaling_NaN(); |
90 | } |
91 | #endif |
92 | |
93 | // Quiet NaN |
94 | constexpr Q_DECL_CONST_FUNCTION static inline double qt_qnan() noexcept |
95 | { |
96 | static_assert(std::numeric_limits<double>::has_quiet_NaN, |
97 | "platform has no definition for quiet NaN for type double" ); |
98 | return std::numeric_limits<double>::quiet_NaN(); |
99 | } |
100 | |
101 | Q_DECL_CONST_FUNCTION static inline bool qt_is_inf(double d) |
102 | { |
103 | return qnumeric_std_wrapper::isinf(d); |
104 | } |
105 | |
106 | Q_DECL_CONST_FUNCTION static inline bool qt_is_nan(double d) |
107 | { |
108 | return qnumeric_std_wrapper::isnan(d); |
109 | } |
110 | |
111 | Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(double d) |
112 | { |
113 | return qnumeric_std_wrapper::isfinite(d); |
114 | } |
115 | |
116 | Q_DECL_CONST_FUNCTION static inline int qt_fpclassify(double d) |
117 | { |
118 | return qnumeric_std_wrapper::fpclassify(d); |
119 | } |
120 | |
121 | Q_DECL_CONST_FUNCTION static inline bool qt_is_inf(float f) |
122 | { |
123 | return qnumeric_std_wrapper::isinf(f); |
124 | } |
125 | |
126 | Q_DECL_CONST_FUNCTION static inline bool qt_is_nan(float f) |
127 | { |
128 | return qnumeric_std_wrapper::isnan(f); |
129 | } |
130 | |
131 | Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f) |
132 | { |
133 | return qnumeric_std_wrapper::isfinite(f); |
134 | } |
135 | |
136 | Q_DECL_CONST_FUNCTION static inline int qt_fpclassify(float f) |
137 | { |
138 | return qnumeric_std_wrapper::fpclassify(f); |
139 | } |
140 | |
141 | #ifndef Q_QDOC |
142 | namespace { |
143 | /*! |
144 | Returns true if the double \a v can be converted to type \c T, false if |
145 | it's out of range. If the conversion is successful, the converted value is |
146 | stored in \a value; if it was not successful, \a value will contain the |
147 | minimum or maximum of T, depending on the sign of \a d. If \c T is |
148 | unsigned, then \a value contains the absolute value of \a v. |
149 | |
150 | This function works for v containing infinities, but not NaN. It's the |
151 | caller's responsibility to exclude that possibility before calling it. |
152 | */ |
153 | template<typename T> |
154 | static inline bool convertDoubleTo(double v, T *value, bool allow_precision_upgrade = true) |
155 | { |
156 | static_assert(std::numeric_limits<T>::is_integer); |
157 | static_assert(std::is_integral_v<T>); |
158 | constexpr bool TypeIsLarger = std::numeric_limits<T>::digits > std::numeric_limits<double>::digits; |
159 | |
160 | if constexpr (TypeIsLarger) { |
161 | using S = std::make_signed_t<T>; |
162 | constexpr S max_mantissa = S(1) << std::numeric_limits<double>::digits; |
163 | // T has more bits than double's mantissa, so don't allow "upgrading" |
164 | // to T (makes it look like the number had more precision than really |
165 | // was transmitted) |
166 | if (!allow_precision_upgrade && !(v <= double(max_mantissa) && v >= double(-max_mantissa - 1))) |
167 | return false; |
168 | } |
169 | |
170 | constexpr T Tmin = (std::numeric_limits<T>::min)(); |
171 | constexpr T Tmax = (std::numeric_limits<T>::max)(); |
172 | |
173 | // The [conv.fpint] (7.10 Floating-integral conversions) section of the C++ |
174 | // standard says only exact conversions are guaranteed. Converting |
175 | // integrals to floating-point with loss of precision has implementation- |
176 | // defined behavior whether the next higher or next lower is returned; |
177 | // converting FP to integral is UB if it can't be represented. |
178 | // |
179 | // That means we can't write UINT64_MAX+1. Writing ldexp(1, 64) would be |
180 | // correct, but Clang, ICC and MSVC don't realize that it's a constant and |
181 | // the math call stays in the compiled code. |
182 | |
183 | #if defined(Q_PROCESSOR_X86_64) && defined(__SSE2__) |
184 | // Of course, UB doesn't apply if we use intrinsics, in which case we are |
185 | // allowed to dpeend on exactly the processor's behavior. This |
186 | // implementation uses the truncating conversions from Scalar Double to |
187 | // integral types (CVTTSD2SI and VCVTTSD2USI), which is documented to |
188 | // return the "indefinite integer value" if the range of the target type is |
189 | // exceeded. (only implemented for x86-64 to avoid having to deal with the |
190 | // non-existence of the 64-bit intrinsics on i386) |
191 | |
192 | if (std::numeric_limits<T>::is_signed) { |
193 | __m128d mv = _mm_set_sd(w: v); |
194 | # ifdef __AVX512F__ |
195 | // use explicit round control and suppress exceptions |
196 | if (sizeof(T) > 4) |
197 | *value = T(_mm_cvtt_roundsd_i64(mv, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); |
198 | else |
199 | *value = _mm_cvtt_roundsd_i32(mv, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); |
200 | # else |
201 | *value = sizeof(T) > 4 ? T(_mm_cvttsd_si64(a: mv)) : _mm_cvttsd_si32(a: mv); |
202 | # endif |
203 | |
204 | // if *value is the "indefinite integer value", check if the original |
205 | // variable \a v is the same value (Tmin is an exact representation) |
206 | if (*value == Tmin && !_mm_ucomieq_sd(mv, _mm_set_sd(Tmin))) { |
207 | // v != Tmin, so it was out of range |
208 | if (v > 0) |
209 | *value = Tmax; |
210 | return false; |
211 | } |
212 | |
213 | // convert the integer back to double and compare for equality with v, |
214 | // to determine if we've lost any precision |
215 | __m128d mi = _mm_setzero_pd(); |
216 | mi = sizeof(T) > 4 ? _mm_cvtsi64_sd(mv, *value) : _mm_cvtsi32_sd(mv, *value); |
217 | return _mm_ucomieq_sd(a: mv, b: mi); |
218 | } |
219 | |
220 | # ifdef __AVX512F__ |
221 | if (!std::numeric_limits<T>::is_signed) { |
222 | // Same thing as above, but this function operates on absolute values |
223 | // and the "indefinite integer value" for the 64-bit unsigned |
224 | // conversion (Tmax) is not representable in double, so it can never be |
225 | // the result of an in-range conversion. This is implemented for AVX512 |
226 | // and later because of the unsigned conversion instruction. Converting |
227 | // to unsigned without losing an extra bit of precision prior to AVX512 |
228 | // is left to the compiler below. |
229 | |
230 | v = fabs(v); |
231 | __m128d mv = _mm_set_sd(v); |
232 | |
233 | // use explicit round control and suppress exceptions |
234 | if (sizeof(T) > 4) |
235 | *value = T(_mm_cvtt_roundsd_u64(mv, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)); |
236 | else |
237 | *value = _mm_cvtt_roundsd_u32(mv, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); |
238 | |
239 | if (*value == Tmax) { |
240 | // no double can have an exact value of quint64(-1), but they can |
241 | // quint32(-1), so we need to compare for that |
242 | if (TypeIsLarger || _mm_ucomieq_sd(mv, _mm_set_sd(Tmax))) |
243 | return false; |
244 | } |
245 | |
246 | // return true if it was an exact conversion |
247 | __m128d mi = _mm_setzero_pd(); |
248 | mi = sizeof(T) > 4 ? _mm_cvtu64_sd(mv, *value) : _mm_cvtu32_sd(mv, *value); |
249 | return _mm_ucomieq_sd(mv, mi); |
250 | } |
251 | # endif |
252 | #endif |
253 | |
254 | double supremum; |
255 | if (std::numeric_limits<T>::is_signed) { |
256 | supremum = -1.0 * Tmin; // -1 * (-2^63) = 2^63, exact (for T = qint64) |
257 | *value = Tmin; |
258 | if (v < Tmin) |
259 | return false; |
260 | } else { |
261 | using ST = typename std::make_signed<T>::type; |
262 | supremum = -2.0 * (std::numeric_limits<ST>::min)(); // -2 * (-2^63) = 2^64, exact (for T = quint64) |
263 | v = fabs(x: v); |
264 | } |
265 | |
266 | *value = Tmax; |
267 | if (v >= supremum) |
268 | return false; |
269 | |
270 | // Now we can convert, these two conversions cannot be UB |
271 | *value = T(v); |
272 | |
273 | QT_WARNING_PUSH |
274 | QT_WARNING_DISABLE_FLOAT_COMPARE |
275 | |
276 | return *value == v; |
277 | |
278 | QT_WARNING_POP |
279 | } |
280 | |
281 | template <typename T> inline bool add_overflow(T v1, T v2, T *r) { return qAddOverflow(v1, v2, r); } |
282 | template <typename T> inline bool sub_overflow(T v1, T v2, T *r) { return qSubOverflow(v1, v2, r); } |
283 | template <typename T> inline bool mul_overflow(T v1, T v2, T *r) { return qMulOverflow(v1, v2, r); } |
284 | |
285 | template <typename T, T V2> bool add_overflow(T v1, std::integral_constant<T, V2>, T *r) |
286 | { |
287 | return qAddOverflow<T, V2>(v1, std::integral_constant<T, V2>{}, r); |
288 | } |
289 | |
290 | template <auto V2, typename T> bool add_overflow(T v1, T *r) |
291 | { |
292 | return qAddOverflow<V2, T>(v1, r); |
293 | } |
294 | |
295 | template <typename T, T V2> bool sub_overflow(T v1, std::integral_constant<T, V2>, T *r) |
296 | { |
297 | return qSubOverflow<T, V2>(v1, std::integral_constant<T, V2>{}, r); |
298 | } |
299 | |
300 | template <auto V2, typename T> bool sub_overflow(T v1, T *r) |
301 | { |
302 | return qSubOverflow<V2, T>(v1, r); |
303 | } |
304 | |
305 | template <typename T, T V2> bool mul_overflow(T v1, std::integral_constant<T, V2>, T *r) |
306 | { |
307 | return qMulOverflow<T, V2>(v1, std::integral_constant<T, V2>{}, r); |
308 | } |
309 | |
310 | template <auto V2, typename T> bool mul_overflow(T v1, T *r) |
311 | { |
312 | return qMulOverflow<V2, T>(v1, r); |
313 | } |
314 | } |
315 | #endif // Q_QDOC |
316 | |
317 | /* |
318 | Safely narrows \a x to \c{To}. Let \c L be |
319 | \c{std::numeric_limit<To>::min()} and \c H be \c{std::numeric_limit<To>::max()}. |
320 | |
321 | If \a x is less than L, returns L. If \a x is greater than H, |
322 | returns H. Otherwise, returns \c{To(x)}. |
323 | */ |
324 | template <typename To, typename From> |
325 | static constexpr auto qt_saturate(From x) |
326 | { |
327 | static_assert(std::is_integral_v<To>); |
328 | static_assert(std::is_integral_v<From>); |
329 | |
330 | [[maybe_unused]] |
331 | constexpr auto Lo = (std::numeric_limits<To>::min)(); |
332 | constexpr auto Hi = (std::numeric_limits<To>::max)(); |
333 | |
334 | if constexpr (std::is_signed_v<From> == std::is_signed_v<To>) { |
335 | // same signedness, we can accept regular integer conversion rules |
336 | return x < Lo ? Lo : |
337 | x > Hi ? Hi : |
338 | /*else*/ To(x); |
339 | } else { |
340 | if constexpr (std::is_signed_v<From>) { // ie. !is_signed_v<To> |
341 | if (x < From{0}) |
342 | return To{0}; |
343 | } |
344 | |
345 | // from here on, x >= 0 |
346 | using FromU = std::make_unsigned_t<From>; |
347 | using ToU = std::make_unsigned_t<To>; |
348 | return FromU(x) > ToU(Hi) ? Hi : To(x); // assumes Hi >= 0 |
349 | } |
350 | } |
351 | |
352 | QT_END_NAMESPACE |
353 | |
354 | #endif // QNUMERIC_P_H |
355 | |