diff options
Diffstat (limited to 'src/corelib/global/qnumeric_p.h')
-rw-r--r-- | src/corelib/global/qnumeric_p.h | 280 |
1 files changed, 165 insertions, 115 deletions
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h index b18d521d05..a6b01b796f 100644 --- a/src/corelib/global/qnumeric_p.h +++ b/src/corelib/global/qnumeric_p.h @@ -47,7 +47,7 @@ // #include "QtCore/qglobal.h" - +#include <cmath> #include <limits> #if defined(Q_CC_MSVC) && !defined(Q_OS_WINCE) @@ -56,153 +56,112 @@ # include <immintrin.h> // for _addcarry_u<nn> #endif -#ifndef __has_builtin -# define __has_builtin(x) 0 +#if defined(Q_CC_MSVC) +#include <float.h> #endif +#if !defined(Q_CC_MSVC) && (defined(Q_OS_QNX) || !defined(__cplusplus) || __cplusplus < 201103L) +#include <math.h> QT_BEGIN_NAMESPACE - -#if !defined(Q_CC_MIPS) - -static const union { unsigned char c[8]; double d; } qt_be_inf_bytes = { { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 } }; -static const union { unsigned char c[8]; double d; } qt_le_inf_bytes = { { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f } }; -static inline double qt_inf() -{ - return (QSysInfo::ByteOrder == QSysInfo::BigEndian - ? qt_be_inf_bytes.d - : qt_le_inf_bytes.d); +namespace qnumeric_std_wrapper { +// the 'using namespace std' below is cases where the stdlib already put the math.h functions in the std namespace and undefined the macros. +static inline bool math_h_isnan(double d) { using namespace std; return isnan(d); } +static inline bool math_h_isinf(double d) { using namespace std; return isinf(d); } +static inline bool math_h_isfinite(double d) { using namespace std; return isfinite(d); } +static inline bool math_h_isnan(float f) { using namespace std; return isnan(f); } +static inline bool math_h_isinf(float f) { using namespace std; return isinf(f); } +static inline bool math_h_isfinite(float f) { using namespace std; return isfinite(f); } } +QT_END_NAMESPACE +// These macros from math.h conflict with the real functions in the std namespace. +#undef signbit +#undef isnan +#undef isinf +#undef isfinite +#endif -// Signaling NAN -static const union { unsigned char c[8]; double d; } qt_be_snan_bytes = { { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 } }; -static const union { unsigned char c[8]; double d; } qt_le_snan_bytes = { { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f } }; -static inline double qt_snan() -{ - return (QSysInfo::ByteOrder == QSysInfo::BigEndian - ? qt_be_snan_bytes.d - : qt_le_snan_bytes.d); -} +QT_BEGIN_NAMESPACE -// Quiet NAN -static const union { unsigned char c[8]; double d; } qt_be_qnan_bytes = { { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 } }; -static const union { unsigned char c[8]; double d; } qt_le_qnan_bytes = { { 0, 0, 0, 0, 0, 0, 0xf8, 0xff } }; -static inline double qt_qnan() -{ - return (QSysInfo::ByteOrder == QSysInfo::BigEndian - ? qt_be_qnan_bytes.d - : qt_le_qnan_bytes.d); +namespace qnumeric_std_wrapper { +#if defined(Q_CC_MSVC) && _MSC_VER < 1800 +static inline bool isnan(double d) { return !!_isnan(d); } +static inline bool isinf(double d) { return !_finite(d) && !_isnan(d); } +static inline bool isfinite(double d) { return !!_finite(d); } +static inline bool isnan(float f) { return !!_isnanf(f); } +static inline bool isinf(float f) { return !_finitef(f) && !_isnan(f); } +static inline bool isfinite(float f) { return !!_finite(f); } +#elif !defined(Q_CC_MSVC) && (defined(Q_OS_QNX) || !defined(__cplusplus) || __cplusplus < 201103L) +static inline bool isnan(double d) { return math_h_isnan(d); } +static inline bool isinf(double d) { return math_h_isinf(d); } +static inline bool isfinite(double d) { return math_h_isfinite(d); } +static inline bool isnan(float f) { return math_h_isnan(f); } +static inline bool isinf(float f) { return math_h_isinf(f); } +static inline bool isfinite(float f) { return math_h_isfinite(f); } +#else +static inline bool isnan(double d) { return std::isnan(d); } +static inline bool isinf(double d) { return std::isinf(d); } +static inline bool isfinite(double d) { return std::isfinite(d); } +static inline bool isnan(float f) { return std::isnan(f); } +static inline bool isinf(float f) { return std::isinf(f); } +static inline bool isfinite(float f) { return std::isfinite(f); } +#endif } -#else // Q_CC_MIPS - -static const unsigned char qt_be_inf_bytes[] = { 0x7f, 0xf0, 0, 0, 0, 0, 0, 0 }; -static const unsigned char qt_le_inf_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf0, 0x7f }; -static inline double qt_inf() +Q_DECL_CONSTEXPR static inline double qt_inf() Q_DECL_NOEXCEPT { - const unsigned char *bytes; - bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian - ? qt_be_inf_bytes - : qt_le_inf_bytes); - - union { unsigned char c[8]; double d; } returnValue; - memcpy(returnValue.c, bytes, sizeof(returnValue.c)); - return returnValue.d; + Q_STATIC_ASSERT_X(std::numeric_limits<double>::has_infinity, + "platform has no definition for infinity for type double"); + return std::numeric_limits<double>::infinity(); } -// Signaling NAN -static const unsigned char qt_be_snan_bytes[] = { 0x7f, 0xf8, 0, 0, 0, 0, 0, 0 }; -static const unsigned char qt_le_snan_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f }; -static inline double qt_snan() +// Signaling NaN +Q_DECL_CONSTEXPR static inline double qt_snan() Q_DECL_NOEXCEPT { - const unsigned char *bytes; - bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian - ? qt_be_snan_bytes - : qt_le_snan_bytes); - - union { unsigned char c[8]; double d; } returnValue; - memcpy(returnValue.c, bytes, sizeof(returnValue.c)); - return returnValue.d; + Q_STATIC_ASSERT_X(std::numeric_limits<double>::has_signaling_NaN, + "platform has no definition for signaling NaN for type double"); + return std::numeric_limits<double>::signaling_NaN(); } -// Quiet NAN -static const unsigned char qt_be_qnan_bytes[] = { 0xff, 0xf8, 0, 0, 0, 0, 0, 0 }; -static const unsigned char qt_le_qnan_bytes[] = { 0, 0, 0, 0, 0, 0, 0xf8, 0xff }; -static inline double qt_qnan() +// Quiet NaN +Q_DECL_CONSTEXPR static inline double qt_qnan() Q_DECL_NOEXCEPT { - const unsigned char *bytes; - bytes = (QSysInfo::ByteOrder == QSysInfo::BigEndian - ? qt_be_qnan_bytes - : qt_le_qnan_bytes); - - union { unsigned char c[8]; double d; } returnValue; - memcpy(returnValue.c, bytes, sizeof(returnValue.c)); - return returnValue.d; + Q_STATIC_ASSERT_X(std::numeric_limits<double>::has_quiet_NaN, + "platform has no definition for quiet NaN for type double"); + return std::numeric_limits<double>::quiet_NaN(); } -#endif // Q_CC_MIPS - static inline bool qt_is_inf(double d) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) == 0x7f && ch[1] == 0xf0; - } else { - return (ch[7] & 0x7f) == 0x7f && ch[6] == 0xf0; - } + return qnumeric_std_wrapper::isinf(d); } static inline bool qt_is_nan(double d) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) == 0x7f && ch[1] > 0xf0; - } else { - return (ch[7] & 0x7f) == 0x7f && ch[6] > 0xf0; - } + return qnumeric_std_wrapper::isnan(d); } static inline bool qt_is_finite(double d) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0xf0) != 0xf0; - } else { - return (ch[7] & 0x7f) != 0x7f || (ch[6] & 0xf0) != 0xf0; - } + return qnumeric_std_wrapper::isfinite(d); } -static inline bool qt_is_inf(float d) +static inline bool qt_is_inf(float f) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) == 0x7f && ch[1] == 0x80; - } else { - return (ch[3] & 0x7f) == 0x7f && ch[2] == 0x80; - } + return qnumeric_std_wrapper::isinf(f); } -static inline bool qt_is_nan(float d) +static inline bool qt_is_nan(float f) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) == 0x7f && ch[1] > 0x80; - } else { - return (ch[3] & 0x7f) == 0x7f && ch[2] > 0x80; - } + return qnumeric_std_wrapper::isnan(f); } -static inline bool qt_is_finite(float d) +static inline bool qt_is_finite(float f) { - uchar *ch = (uchar *)&d; - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - return (ch[0] & 0x7f) != 0x7f || (ch[1] & 0x80) != 0x80; - } else { - return (ch[3] & 0x7f) != 0x7f || (ch[2] & 0x80) != 0x80; - } + return qnumeric_std_wrapper::isfinite(f); } // -// Overflow math +// Unsigned overflow math // namespace { template <typename T> inline typename QtPrivate::QEnableIf<QtPrivate::is_unsigned<T>::value, bool>::Type @@ -230,28 +189,28 @@ mul_overflow(T v1, T v2, T *r) #endif // GCC 5 and Clang have builtins to detect overflows -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uadd_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uadd_overflow) template <> inline bool add_overflow(unsigned v1, unsigned v2, unsigned *r) { return __builtin_uadd_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uaddl_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uaddl_overflow) template <> inline bool add_overflow(unsigned long v1, unsigned long v2, unsigned long *r) { return __builtin_uaddl_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_uaddll_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_uaddll_overflow) template <> inline bool add_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r) { return __builtin_uaddll_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umul_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umul_overflow) template <> inline bool mul_overflow(unsigned v1, unsigned v2, unsigned *r) { return __builtin_umul_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umull_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umull_overflow) template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigned long *r) { return __builtin_umull_overflow(v1, v2, r); } #endif -#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || __has_builtin(__builtin_umulll_overflow) +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_umulll_overflow) template <> inline bool mul_overflow(unsigned long long v1, unsigned long long v2, unsigned long long *r) { return __builtin_umulll_overflow(v1, v2, r); } # define HAVE_MUL64_OVERFLOW @@ -303,6 +262,97 @@ template <> inline bool mul_overflow(unsigned long v1, unsigned long v2, unsigne #else # undef HAVE_MUL64_OVERFLOW #endif + +// +// Signed overflow math +// +// In C++, signed overflow math is Undefined Behavior. However, many CPUs do implement some way to +// check for overflow. Some compilers expose intrinsics to use this functionality. If the no +// intrinsic is exposed, overflow checking can be done by widening the result type and "manually" +// checking for overflow. Or, alternatively, by using inline assembly to use the CPU features. +// +// Only int overflow checking is implemented, because it's the only one used. +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_sadd_overflow) +inline bool add_overflow(int v1, int v2, int *r) +{ return __builtin_sadd_overflow(v1, v2, r); } +#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) +inline bool add_overflow(int v1, int v2, int *r) +{ + quint8 overflow = 0; + int res = v1; + + asm ("addl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (res) + : "r" (v2), "1" (res) + : "cc" + ); + *r = res; + return overflow; +} +#else +inline bool add_overflow(int v1, int v2, int *r) +{ + qint64 t = qint64(v1) + v2; + *r = static_cast<int>(t); + return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min(); +} +#endif + +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_ssub_overflow) +inline bool sub_overflow(int v1, int v2, int *r) +{ return __builtin_ssub_overflow(v1, v2, r); } +#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) +inline bool sub_overflow(int v1, int v2, int *r) +{ + quint8 overflow = 0; + int res = v1; + + asm ("subl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (res) + : "r" (v2), "1" (res) + : "cc" + ); + *r = res; + return overflow; +} +#else +inline bool sub_overflow(int v1, int v2, int *r) +{ + qint64 t = qint64(v1) - v2; + *r = static_cast<int>(t); + return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min(); +} +#endif + +#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 500) || QT_HAS_BUILTIN(__builtin_smul_overflow) +inline bool mul_overflow(int v1, int v2, int *r) +{ return __builtin_smul_overflow(v1, v2, r); } +#elif defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) +inline bool mul_overflow(int v1, int v2, int *r) +{ + quint8 overflow = 0; + int res = v1; + + asm ("imul %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (res) + : "r" (v2), "1" (res) + : "cc" + ); + *r = res; + return overflow; +} +#else +inline bool mul_overflow(int v1, int v2, int *r) +{ + qint64 t = qint64(v1) * v2; + *r = static_cast<int>(t); + return t > std::numeric_limits<int>::max() || t < std::numeric_limits<int>::min(); +} +#endif + } QT_END_NAMESPACE |