From 84aea6c091d020a37c2b452a6f56ca27b3b2c7cb Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 13 Mar 2019 18:49:04 +0100 Subject: Add qFpClassify() to mirror std::fpclassify() The rules of std don't permit us to add an overload for fpclassify(qfloat16), so we need our own equivalent that we *can* overload. Deploy it in the few places we use fpclassify(). Extended qnumeric's testing to cover qFpClassify(). Change-Id: Ie5a0a5cc24599d1571404c573d33c682b0d305a5 Reviewed-by: Thiago Macieira Reviewed-by: Paul Wicking --- src/corelib/global/qnumeric.cpp | 25 ++++++++++++++++- src/corelib/global/qnumeric.h | 4 ++- src/corelib/global/qnumeric_p.h | 19 ++++++++++++- src/corelib/kernel/qvariant.cpp | 6 ++-- src/testlib/qtestcase.cpp | 10 +++---- .../auto/corelib/global/qnumeric/tst_qnumeric.cpp | 32 +++++++++++++++++++++- 6 files changed, 84 insertions(+), 12 deletions(-) diff --git a/src/corelib/global/qnumeric.cpp b/src/corelib/global/qnumeric.cpp index fc2b052edf..e6ba62f530 100644 --- a/src/corelib/global/qnumeric.cpp +++ b/src/corelib/global/qnumeric.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE /*! Returns \c true if the double \a {d} is equivalent to infinity. \relates + \sa qInf() */ Q_CORE_EXPORT bool qIsInf(double d) { return qt_is_inf(d); } @@ -64,6 +65,7 @@ Q_CORE_EXPORT bool qIsFinite(double d) { return qt_is_finite(d); } /*! Returns \c true if the float \a {f} is equivalent to infinity. \relates + \sa qInf() */ Q_CORE_EXPORT bool qIsInf(float f) { return qt_is_inf(f); } @@ -88,15 +90,36 @@ Q_CORE_EXPORT double qSNaN() { return qt_snan(); } /*! Returns the bit pattern of a quiet NaN as a double. \relates + \sa qIsNaN() */ Q_CORE_EXPORT double qQNaN() { return qt_qnan(); } /*! Returns the bit pattern for an infinite number as a double. \relates + \sa qIsInf() */ Q_CORE_EXPORT double qInf() { return qt_inf(); } +/*! + \relates + Classifies a floating-point value. + + The return values are defined in \c{}: returns one of the following, + determined by the floating-point class of \a val: + \list + \li FP_NAN not a number + \li FP_INFINITE infinities (positive or negative) + \li FP_NORMAL finite with a full mantissa + \li FP_SUBNORMAL finite with a reduced mantissa + \endlist +*/ +Q_CORE_EXPORT int qFpClassify(double val) { return qt_fpclassify(val); } + +/*! + \overload +*/ +Q_CORE_EXPORT int qFpClassify(float val) { return qt_fpclassify(val); } /*! diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h index 535a96aaec..6a0c64712f 100644 --- a/src/corelib/global/qnumeric.h +++ b/src/corelib/global/qnumeric.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -48,9 +48,11 @@ QT_BEGIN_NAMESPACE Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsInf(double d); Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsNaN(double d); Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(double d); +Q_CORE_EXPORT Q_DECL_CONST_FUNCTION int qFpClassify(double val); Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsInf(float f); Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsNaN(float f); Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(float f); +Q_CORE_EXPORT Q_DECL_CONST_FUNCTION int qFpClassify(float val); Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qSNaN(); Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qQNaN(); Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qInf(); diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h index 4a225b2599..56e670c477 100644 --- a/src/corelib/global/qnumeric_p.h +++ b/src/corelib/global/qnumeric_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -85,9 +85,11 @@ namespace qnumeric_std_wrapper { Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(double d) { using namespace std; return isnan(d); } Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(double d) { using namespace std; return isinf(d); } Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(double d) { using namespace std; return isfinite(d); } +Q_DECL_CONST_FUNCTION static inline int math_h_fpclassify(double d) { using namespace std; return fpclassify(d); } Q_DECL_CONST_FUNCTION static inline bool math_h_isnan(float f) { using namespace std; return isnan(f); } Q_DECL_CONST_FUNCTION static inline bool math_h_isinf(float f) { using namespace std; return isinf(f); } Q_DECL_CONST_FUNCTION static inline bool math_h_isfinite(float f) { using namespace std; return isfinite(f); } +Q_DECL_CONST_FUNCTION static inline int math_h_fpclassify(float f) { using namespace std; return fpclassify(f); } } QT_END_NAMESPACE // These macros from math.h conflict with the real functions in the std namespace. @@ -95,6 +97,7 @@ QT_END_NAMESPACE # undef isnan # undef isinf # undef isfinite +# undef fpclassify # endif // defined(isnan) #endif @@ -106,16 +109,20 @@ namespace qnumeric_std_wrapper { Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return math_h_isnan(d); } Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return math_h_isinf(d); } Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return math_h_isfinite(d); } +Q_DECL_CONST_FUNCTION static inline int fpclassify(double d) { return math_h_fpclassify(d); } Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return math_h_isnan(f); } Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return math_h_isinf(f); } Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return math_h_isfinite(f); } +Q_DECL_CONST_FUNCTION static inline int fpclassify(float f) { return math_h_fpclassify(f); } #else Q_DECL_CONST_FUNCTION static inline bool isnan(double d) { return std::isnan(d); } Q_DECL_CONST_FUNCTION static inline bool isinf(double d) { return std::isinf(d); } Q_DECL_CONST_FUNCTION static inline bool isfinite(double d) { return std::isfinite(d); } +Q_DECL_CONST_FUNCTION static inline int fpclassify(double d) { return std::fpclassify(d); } Q_DECL_CONST_FUNCTION static inline bool isnan(float f) { return std::isnan(f); } Q_DECL_CONST_FUNCTION static inline bool isinf(float f) { return std::isinf(f); } Q_DECL_CONST_FUNCTION static inline bool isfinite(float f) { return std::isfinite(f); } +Q_DECL_CONST_FUNCTION static inline int fpclassify(float f) { return std::fpclassify(f); } #endif } @@ -157,6 +164,11 @@ Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(double d) return qnumeric_std_wrapper::isfinite(d); } +Q_DECL_CONST_FUNCTION static inline int qt_fpclassify(double d) +{ + return qnumeric_std_wrapper::fpclassify(d); +} + Q_DECL_CONST_FUNCTION static inline bool qt_is_inf(float f) { return qnumeric_std_wrapper::isinf(f); @@ -172,6 +184,11 @@ Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f) return qnumeric_std_wrapper::isfinite(f); } +Q_DECL_CONST_FUNCTION static inline int qt_fpclassify(float f) +{ + return qnumeric_std_wrapper::fpclassify(f); +} + #ifndef Q_CLANG_QDOC namespace { /*! diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 18c7f7648d..deba977f77 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2018 Intel Corporation. ** Copyright (C) 2015 Olivier Goffart ** Contact: https://www.qt.io/licensing/ @@ -4002,8 +4002,8 @@ static int numericCompare(const QVariant::Private *d1, const QVariant::Private * return 0; // only do fuzzy comparisons for finite, non-zero numbers - int c1 = std::fpclassify(r1); - int c2 = std::fpclassify(r2); + int c1 = qFpClassify(r1); + int c2 = qFpClassify(r2); if ((c1 == FP_NORMAL || c1 == FP_SUBNORMAL) && (c2 == FP_NORMAL || c2 == FP_SUBNORMAL)) { if (qFuzzyCompare(r1, r2)) return 0; diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index a010ea467b..d9f8dd7805 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -2522,12 +2522,12 @@ bool QTest::compare_helper(bool success, const char *failureMsg, template static bool floatingCompare(const T &t1, const T &t2) { - switch (std::fpclassify(t1)) + switch (qFpClassify(t1)) { case FP_INFINITE: - return (t1 < 0) == (t2 < 0) && std::fpclassify(t2) == FP_INFINITE; + return (t1 < 0) == (t2 < 0) && qFpClassify(t2) == FP_INFINITE; case FP_NAN: - return std::fpclassify(t2) == FP_NAN; + return qFpClassify(t2) == FP_NAN; default: return qFuzzyCompare(t1, t2); } @@ -2631,7 +2631,7 @@ static void massageExponent(char *text) template <> Q_TESTLIB_EXPORT char *QTest::toString(const TYPE &t) \ { \ char *msg = new char[128]; \ - switch (std::fpclassify(t)) { \ + switch (qFpClassify(t)) { \ case FP_INFINITE: \ qstrncpy(msg, (t < 0 ? "-inf" : "inf"), 128); \ break; \ diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp index f37dcbbbba..e567e465f7 100644 --- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp +++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -43,6 +43,7 @@ private slots: void fuzzyCompare_data(); void fuzzyCompare(); void qNanInf(); + void classifyfp(); void floatDistance_data(); void floatDistance(); void floatDistance_double_data(); @@ -123,6 +124,7 @@ void tst_QNumeric::qNanInf() QVERIFY(qIsNaN(-nan)); QVERIFY(!(nan == nan)); QVERIFY(qIsNaN(0.0 * nan)); + QCOMPARE(qFpClassify(nan), FP_NAN); QCOMPARE(nan, nan); QCOMPARE(nan, -nan); QCOMPARE(nan, qQNaN()); @@ -143,6 +145,34 @@ void tst_QNumeric::qNanInf() QVERIFY(qIsNaN(0.0 * inf)); } +void tst_QNumeric::classifyfp() +{ + QCOMPARE(qFpClassify(qQNaN()), FP_NAN); + + QCOMPARE(qFpClassify(qInf()), FP_INFINITE); + QCOMPARE(qFpClassify(-qInf()), FP_INFINITE); + QCOMPARE(qFpClassify(DBL_MAX * 2.0), FP_INFINITE); + QCOMPARE(qFpClassify(FLT_MAX * 2.f), FP_INFINITE); + QCOMPARE(qFpClassify(DBL_MAX * -2.0), FP_INFINITE); + QCOMPARE(qFpClassify(FLT_MAX * -2.f), FP_INFINITE); + + QCOMPARE(qFpClassify(1.0), FP_NORMAL); + QCOMPARE(qFpClassify(DBL_MAX), FP_NORMAL); + QCOMPARE(qFpClassify(-DBL_MAX), FP_NORMAL); + QCOMPARE(qFpClassify(DBL_MIN), FP_NORMAL); + QCOMPARE(qFpClassify(-DBL_MIN), FP_NORMAL); + QCOMPARE(qFpClassify(DBL_MIN / 2.0), FP_SUBNORMAL); + QCOMPARE(qFpClassify(DBL_MIN / -2.0), FP_SUBNORMAL); + + QCOMPARE(qFpClassify(1.f), FP_NORMAL); + QCOMPARE(qFpClassify(FLT_MAX), FP_NORMAL); + QCOMPARE(qFpClassify(-FLT_MAX), FP_NORMAL); + QCOMPARE(qFpClassify(FLT_MIN), FP_NORMAL); + QCOMPARE(qFpClassify(-FLT_MIN), FP_NORMAL); + QCOMPARE(qFpClassify(FLT_MIN / 2.f), FP_SUBNORMAL); + QCOMPARE(qFpClassify(FLT_MIN / -2.f), FP_SUBNORMAL); +} + void tst_QNumeric::floatDistance_data() { QTest::addColumn("val1"); -- cgit v1.2.3