From a2b58c1643b5c8b67cb18750ef2d565180a3a179 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 10 Aug 2015 13:15:24 +0200 Subject: [PATCH] Implement qt_is_{inf,nan,finite} using std. library functions. The previous implementations did not check the full mantissa. The result was that certain NaN values were seen as +/-Infinity. A nice benefit is that the generated code for this implementation is also faster. Task-number: QTBUG-47692 Change-Id: I1507ec579ccd9a2ab97da8cf83dabbc5d6e28597 Reviewed-by: Lars Knoll --- src/corelib/global/qnumeric_p.h | 99 +++++++++++-------- .../corelib/global/qnumeric/tst_qnumeric.cpp | 12 +++ 2 files changed, 71 insertions(+), 40 deletions(-) diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h index 7b048f6c1fc..b56cdef9391 100644 --- a/src/corelib/global/qnumeric_p.h +++ b/src/corelib/global/qnumeric_p.h @@ -47,7 +47,7 @@ // #include "QtCore/qglobal.h" - +#include #include #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) @@ -56,7 +56,56 @@ # include // for _addcarry_u #endif +#if defined(Q_CC_MSVC) +#include +#endif + +#if !defined(Q_CC_MSVC) && (defined(Q_OS_QNX) || !defined(__cplusplus) || __cplusplus < 201103L) +#include QT_BEGIN_NAMESPACE +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 + +QT_BEGIN_NAMESPACE + +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 +} #if !defined(Q_CC_MIPS) @@ -139,62 +188,32 @@ static inline double qt_qnan() 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); } // diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp index 6be8ff81cf2..01ff3a6c7ec 100644 --- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp +++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp @@ -105,6 +105,18 @@ void tst_QNumeric::qNan() QVERIFY(qIsNaN(nan)); QVERIFY(qIsNaN(nan + 1)); QVERIFY(qIsNaN(-nan)); + + Q_STATIC_ASSERT(sizeof(double) == 8); +#ifdef Q_LITTLE_ENDIAN + const uchar bytes[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f }; +#else + const uchar bytes[] = { 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; +#endif + memcpy(&nan, bytes, 8); + QVERIFY(!qIsFinite(nan)); + QVERIFY(!qIsInf(nan)); + QVERIFY(qIsNaN(nan)); + double inf = qInf(); QVERIFY(inf > 0); QVERIFY(-inf < 0);