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
30QT_BEGIN_NAMESPACE
31namespace 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.
33Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(double d) { using namespace std; return isnan(d); }
34Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(double d) { using namespace std; return isinf(d); }
35Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(double d) { using namespace std; return isfinite(d); }
36Q_DECL_CONST_FUNCTION static inline int math_h_fpclassify(double d) { using namespace std; return fpclassify(d); }
37Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(float f) { using namespace std; return isnan(f); }
38Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(float f) { using namespace std; return isinf(f); }
39Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(float f) { using namespace std; return isfinite(f); }
40Q_DECL_CONST_FUNCTION static inline int math_h_fpclassify(float f) { using namespace std; return fpclassify(f); }
41}
42QT_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
52QT_BEGIN_NAMESPACE
53
54namespace qnumeric_std_wrapper {
55#if defined(QT_MATH_H_DEFINES_MACROS)
56# undef QT_MATH_H_DEFINES_MACROS
57Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return math_h_isnan(d); }
58Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return math_h_isinf(d); }
59Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return math_h_isfinite(d); }
60Q_DECL_CONST_FUNCTION static inline int fpclassify(double d) { return math_h_fpclassify(d); }
61Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return math_h_isnan(f); }
62Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return math_h_isinf(f); }
63Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return math_h_isfinite(f); }
64Q_DECL_CONST_FUNCTION static inline int fpclassify(float f) { return math_h_fpclassify(f); }
65#else
66Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return std::isnan(x: d); }
67Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return std::isinf(x: d); }
68Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return std::isfinite(x: d); }
69Q_DECL_CONST_FUNCTION static inline int fpclassify(double d) { return std::fpclassify(x: d); }
70Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return std::isnan(x: f); }
71Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return std::isinf(x: f); }
72Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return std::isfinite(x: f); }
73Q_DECL_CONST_FUNCTION static inline int fpclassify(float f) { return std::fpclassify(x: f); }
74#endif
75}
76
77constexpr 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)
85constexpr 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
94constexpr 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
101Q_DECL_CONST_FUNCTION static inline bool qt_is_inf(double d)
102{
103 return qnumeric_std_wrapper::isinf(d);
104}
105
106Q_DECL_CONST_FUNCTION static inline bool qt_is_nan(double d)
107{
108 return qnumeric_std_wrapper::isnan(d);
109}
110
111Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(double d)
112{
113 return qnumeric_std_wrapper::isfinite(d);
114}
115
116Q_DECL_CONST_FUNCTION static inline int qt_fpclassify(double d)
117{
118 return qnumeric_std_wrapper::fpclassify(d);
119}
120
121Q_DECL_CONST_FUNCTION static inline bool qt_is_inf(float f)
122{
123 return qnumeric_std_wrapper::isinf(f);
124}
125
126Q_DECL_CONST_FUNCTION static inline bool qt_is_nan(float f)
127{
128 return qnumeric_std_wrapper::isnan(f);
129}
130
131Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f)
132{
133 return qnumeric_std_wrapper::isfinite(f);
134}
135
136Q_DECL_CONST_FUNCTION static inline int qt_fpclassify(float f)
137{
138 return qnumeric_std_wrapper::fpclassify(f);
139}
140
141#ifndef Q_QDOC
142namespace {
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*/
153template<typename T>
154static 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
273QT_WARNING_PUSH
274QT_WARNING_DISABLE_FLOAT_COMPARE
275
276 return *value == v;
277
278QT_WARNING_POP
279}
280
281template <typename T> inline bool add_overflow(T v1, T v2, T *r) { return qAddOverflow(v1, v2, r); }
282template <typename T> inline bool sub_overflow(T v1, T v2, T *r) { return qSubOverflow(v1, v2, r); }
283template <typename T> inline bool mul_overflow(T v1, T v2, T *r) { return qMulOverflow(v1, v2, r); }
284
285template <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
290template <auto V2, typename T> bool add_overflow(T v1, T *r)
291{
292 return qAddOverflow<V2, T>(v1, r);
293}
294
295template <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
300template <auto V2, typename T> bool sub_overflow(T v1, T *r)
301{
302 return qSubOverflow<V2, T>(v1, r);
303}
304
305template <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
310template <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*/
324template <typename To, typename From>
325static 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
352QT_END_NAMESPACE
353
354#endif // QNUMERIC_P_H
355

source code of qtbase/src/corelib/global/qnumeric_p.h