diff options
Diffstat (limited to 'src/corelib/global/qnumeric_p.h')
-rw-r--r-- | src/corelib/global/qnumeric_p.h | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h index 5f8a124bcc..e318c3759b 100644 --- a/src/corelib/global/qnumeric_p.h +++ b/src/corelib/global/qnumeric_p.h @@ -64,6 +64,10 @@ #include <float.h> #endif +# if defined(Q_OS_INTEGRITY) && defined(Q_PROCESSOR_ARM_64) +#include <arm64_ghs.h> +#endif + #if !defined(Q_CC_MSVC) && (defined(Q_OS_QNX) || defined(Q_CC_INTEL)) # include <math.h> # ifdef isnan @@ -163,6 +167,58 @@ Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f) #ifndef Q_CLANG_QDOC namespace { +/*! + Returns true if the double \a v can be converted to type \c T, false if + it's out of range. If the conversion is successful, the converted value is + stored in \a value; if it was not successful, \a value will contain the + minimum or maximum of T, depending on the sign of \a d. If \c T is + unsigned, then \a value contains the absolute value of \a v. + + This function works for v containing infinities, but not NaN. It's the + caller's responsibility to exclude that possibility before calling it. +*/ +template <typename T> static inline bool convertDoubleTo(double v, T *value) +{ + Q_STATIC_ASSERT(std::numeric_limits<T>::is_integer); + + // The [conv.fpint] (7.10 Floating-integral conversions) section of the C++ + // standard says only exact conversions are guaranteed. Converting + // integrals to floating-point with loss of precision has implementation- + // defined behavior whether the next higher or next lower is returned; + // converting FP to integral is UB if it can't be represented. + // + // That means we can't write UINT64_MAX+1. Writing ldexp(1, 64) would be + // correct, but Clang, ICC and MSVC don't realize that it's a constant and + // the math call stays in the compiled code. + + double supremum; + if (std::numeric_limits<T>::is_signed) { + supremum = -1.0 * std::numeric_limits<T>::min(); // -1 * (-2^63) = 2^63, exact (for T = qint64) + *value = std::numeric_limits<T>::min(); + if (v < std::numeric_limits<T>::min()) + return false; + } else { + using ST = typename std::make_signed<T>::type; + supremum = -2.0 * std::numeric_limits<ST>::min(); // -2 * (-2^63) = 2^64, exact (for T = quint64) + v = fabs(v); + } + + *value = std::numeric_limits<T>::max(); + if (v >= supremum) + return false; + + // Now we can convert, these two conversions cannot be UB + *value = T(v); + +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wfloat-equal") +QT_WARNING_DISABLE_CLANG("-Wfloat-equal") + + return *value == v; + +QT_WARNING_POP +} + // Overflow math. // This provides efficient implementations for int, unsigned, qsizetype and // size_t. Implementations for 8- and 16-bit types will work but may not be as @@ -271,6 +327,38 @@ mul_overflow(T v1, T v2, T *r) return lr > std::numeric_limits<T>::max() || lr < std::numeric_limits<T>::min(); } +# if defined(Q_OS_INTEGRITY) && defined(Q_PROCESSOR_ARM_64) +template <> inline bool mul_overflow(quint64 v1, quint64 v2, quint64 *r) +{ + *r = v1 * v2; + return __MULUH64(v1, v2); +} +template <> inline bool mul_overflow(qint64 v1, qint64 v2, qint64 *r) +{ + qint64 high = __MULSH64(v1, v2); + if (high == 0) { + *r = v1 * v2; + return *r < 0; + } + if (high == -1) { + *r = v1 * v2; + return *r >= 0; + } + return true; +} + +template <> inline bool mul_overflow(uint64_t v1, uint64_t v2, uint64_t *r) +{ + return mul_overflow<quint64>(v1,v2,reinterpret_cast<quint64*>(r)); +} + +template <> inline bool mul_overflow(int64_t v1, int64_t v2, int64_t *r) +{ + return mul_overflow<qint64>(v1,v2,reinterpret_cast<qint64*>(r)); +} + +#endif + # if defined(Q_CC_MSVC) && defined(Q_PROCESSOR_X86) // We can use intrinsics for the unsigned operations with MSVC template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r) |