Featurize support for signaling NaN
One of our compilers for emscripten coerces all signaling NaNs to quiet ones, so won't do any actual signaling. Anyone relying on them to do so shall be disappointed, so it's better that they know about it at compile-time - or, at least, have the ability to find it out. Put the signaling NaN producers (and remaining (test) code using them) under the control of a feature that's disabled when numeric_limits claims double has no signaling NaN. Assume the bootstrap library doesn't need signaling NaNs. Sadly, until C++20 <bit>, there's no contexpr way to test that alleged signalling and quiet NaNs are actually distinct. Added some auto-tests for signaling NaN, including that it's distinct from quiet NaN. Any platform on which the last fails should disable this feature. Task-number: QTBUG-77967 Change-Id: I57e9d14bfe276732cd313887adc9acc354d88f08 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
parent
86876744f0
commit
4bd6cd1992
@ -484,6 +484,17 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"signaling_nan": {
|
||||||
|
"label": "Signaling NaN for doubles",
|
||||||
|
"type": "compile",
|
||||||
|
"test": {
|
||||||
|
"head": [ "#include <limits>" ],
|
||||||
|
"main": [
|
||||||
|
"using B = std::numeric_limits<double>;",
|
||||||
|
"static_assert(B::has_signaling_NaN, \"System lacks signaling NaN\");"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"sse2": {
|
"sse2": {
|
||||||
"label": "SSE2 instructions",
|
"label": "SSE2 instructions",
|
||||||
"type": "x86Simd"
|
"type": "x86Simd"
|
||||||
@ -1005,6 +1016,11 @@
|
|||||||
{ "type": "define", "name": "QT_REDUCE_RELOCATIONS" }
|
{ "type": "define", "name": "QT_REDUCE_RELOCATIONS" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"signaling_nan": {
|
||||||
|
"label": "Signaling NaN",
|
||||||
|
"condition": "tests.signaling_nan",
|
||||||
|
"output": [ "publicFeature" ]
|
||||||
|
},
|
||||||
"sse2": {
|
"sse2": {
|
||||||
"label": "SSE2",
|
"label": "SSE2",
|
||||||
"condition": "(arch.i386 || arch.x86_64) && tests.sse2",
|
"condition": "(arch.i386 || arch.x86_64) && tests.sse2",
|
||||||
|
@ -109,6 +109,7 @@
|
|||||||
# define QT_FEATURE_renameat2 -1
|
# define QT_FEATURE_renameat2 -1
|
||||||
#endif
|
#endif
|
||||||
#define QT_FEATURE_sharedmemory -1
|
#define QT_FEATURE_sharedmemory -1
|
||||||
|
#define QT_FEATURE_signaling_nan -1
|
||||||
#define QT_FEATURE_slog2 -1
|
#define QT_FEATURE_slog2 -1
|
||||||
#ifdef __GLIBC_PREREQ
|
#ifdef __GLIBC_PREREQ
|
||||||
# define QT_FEATURE_statx (__GLIBC_PREREQ(2, 28) ? 1 : -1)
|
# define QT_FEATURE_statx (__GLIBC_PREREQ(2, 28) ? 1 : -1)
|
||||||
|
@ -81,11 +81,13 @@ Q_CORE_EXPORT bool qIsNaN(float f) { return qt_is_nan(f); }
|
|||||||
*/
|
*/
|
||||||
Q_CORE_EXPORT bool qIsFinite(float f) { return qt_is_finite(f); }
|
Q_CORE_EXPORT bool qIsFinite(float f) { return qt_is_finite(f); }
|
||||||
|
|
||||||
|
#if QT_CONFIG(signaling_nan)
|
||||||
/*!
|
/*!
|
||||||
Returns the bit pattern of a signalling NaN as a double.
|
Returns the bit pattern of a signalling NaN as a double.
|
||||||
\relates <QtGlobal>
|
\relates <QtGlobal>
|
||||||
*/
|
*/
|
||||||
Q_CORE_EXPORT double qSNaN() { return qt_snan(); }
|
Q_CORE_EXPORT double qSNaN() { return qt_snan(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns the bit pattern of a quiet NaN as a double.
|
Returns the bit pattern of a quiet NaN as a double.
|
||||||
|
@ -44,7 +44,6 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
|
||||||
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsInf(double d);
|
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 qIsNaN(double d);
|
||||||
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(double d);
|
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(double d);
|
||||||
@ -53,7 +52,9 @@ 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 qIsNaN(float f);
|
||||||
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qIsFinite(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 int qFpClassify(float val);
|
||||||
|
#if QT_CONFIG(signaling_nan)
|
||||||
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qSNaN();
|
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qSNaN();
|
||||||
|
#endif
|
||||||
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qQNaN();
|
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qQNaN();
|
||||||
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qInf();
|
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qInf();
|
||||||
|
|
||||||
@ -61,7 +62,9 @@ Q_CORE_EXPORT quint32 qFloatDistance(float a, float b);
|
|||||||
Q_CORE_EXPORT quint64 qFloatDistance(double a, double b);
|
Q_CORE_EXPORT quint64 qFloatDistance(double a, double b);
|
||||||
|
|
||||||
#define Q_INFINITY (QT_PREPEND_NAMESPACE(qInf)())
|
#define Q_INFINITY (QT_PREPEND_NAMESPACE(qInf)())
|
||||||
#define Q_SNAN (QT_PREPEND_NAMESPACE(qSNaN)())
|
#if QT_CONFIG(signaling_nan)
|
||||||
|
# define Q_SNAN (QT_PREPEND_NAMESPACE(qSNaN)())
|
||||||
|
#endif
|
||||||
#define Q_QNAN (QT_PREPEND_NAMESPACE(qQNaN)())
|
#define Q_QNAN (QT_PREPEND_NAMESPACE(qQNaN)())
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -133,13 +133,14 @@ Q_DECL_CONSTEXPR Q_DECL_CONST_FUNCTION static inline double qt_inf() noexcept
|
|||||||
return std::numeric_limits<double>::infinity();
|
return std::numeric_limits<double>::infinity();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signaling NaN
|
#if QT_CONFIG(signaling_nan)
|
||||||
Q_DECL_CONSTEXPR Q_DECL_CONST_FUNCTION static inline double qt_snan() noexcept
|
Q_DECL_CONSTEXPR Q_DECL_CONST_FUNCTION static inline double qt_snan() noexcept
|
||||||
{
|
{
|
||||||
Q_STATIC_ASSERT_X(std::numeric_limits<double>::has_signaling_NaN,
|
Q_STATIC_ASSERT_X(std::numeric_limits<double>::has_signaling_NaN,
|
||||||
"platform has no definition for signaling NaN for type double");
|
"platform has no definition for signaling NaN for type double");
|
||||||
return std::numeric_limits<double>::signaling_NaN();
|
return std::numeric_limits<double>::signaling_NaN();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Quiet NaN
|
// Quiet NaN
|
||||||
Q_DECL_CONSTEXPR Q_DECL_CONST_FUNCTION static inline double qt_qnan() noexcept
|
Q_DECL_CONSTEXPR Q_DECL_CONST_FUNCTION static inline double qt_qnan() noexcept
|
||||||
|
@ -44,6 +44,9 @@ private slots:
|
|||||||
void fuzzyCompare();
|
void fuzzyCompare();
|
||||||
void rawNaN_data();
|
void rawNaN_data();
|
||||||
void rawNaN();
|
void rawNaN();
|
||||||
|
#if QT_CONFIG(signaling_nan)
|
||||||
|
void distinctNaN();
|
||||||
|
#endif
|
||||||
void generalNaN_data();
|
void generalNaN_data();
|
||||||
void generalNaN();
|
void generalNaN();
|
||||||
void infinity();
|
void infinity();
|
||||||
@ -139,6 +142,9 @@ void tst_QNumeric::rawNaN_data()
|
|||||||
QTest::addColumn<double>("nan");
|
QTest::addColumn<double>("nan");
|
||||||
|
|
||||||
QTest::newRow("quiet") << qQNaN();
|
QTest::newRow("quiet") << qQNaN();
|
||||||
|
#if QT_CONFIG(signaling_nan)
|
||||||
|
QTest::newRow("signaling") << qSNaN();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QNumeric::rawNaN()
|
void tst_QNumeric::rawNaN()
|
||||||
@ -147,6 +153,15 @@ void tst_QNumeric::rawNaN()
|
|||||||
checkNaN(nan);
|
checkNaN(nan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if QT_CONFIG(signaling_nan)
|
||||||
|
void tst_QNumeric::distinctNaN()
|
||||||
|
{
|
||||||
|
const double qnan = qQNaN();
|
||||||
|
const double snan = qSNaN();
|
||||||
|
QVERIFY(memcmp(&qnan, &snan, sizeof(double)) != 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void tst_QNumeric::generalNaN_data()
|
void tst_QNumeric::generalNaN_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<int>("most");
|
QTest::addColumn<int>("most");
|
||||||
|
@ -1413,10 +1413,12 @@ void tst_QCborValue::toCbor_data()
|
|||||||
// The rest of these tests are conversions whose decoding does not yield
|
// The rest of these tests are conversions whose decoding does not yield
|
||||||
// back the same QCborValue.
|
// back the same QCborValue.
|
||||||
|
|
||||||
|
#if QT_CONFIG(signaling_nan)
|
||||||
// Signalling NaN get normalized to quiet ones
|
// Signalling NaN get normalized to quiet ones
|
||||||
QTest::newRow("Double:snan") << QCborValue(qSNaN()) << raw("\xfb\x7f\xf8\0""\0\0\0\0\0") << QCborValue::EncodingOptions();
|
QTest::newRow("Double:snan") << QCborValue(qSNaN()) << raw("\xfb\x7f\xf8\0""\0\0\0\0\0") << QCborValue::EncodingOptions();
|
||||||
QTest::newRow("Float:snan") << QCborValue(qSNaN()) << raw("\xfa\x7f\xc0\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat);
|
QTest::newRow("Float:snan") << QCborValue(qSNaN()) << raw("\xfa\x7f\xc0\0\0") << QCborValue::EncodingOptions(QCborValue::UseFloat);
|
||||||
QTest::newRow("Float16:snan") << QCborValue(qSNaN()) << raw("\xf9\x7e\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16);
|
QTest::newRow("Float16:snan") << QCborValue(qSNaN()) << raw("\xf9\x7e\0") << QCborValue::EncodingOptions(QCborValue::UseFloat16);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Floating point written as integers are read back as integers
|
// Floating point written as integers are read back as integers
|
||||||
QTest::newRow("UseInteger:0") << QCborValue(0.) << raw("\x00") << QCborValue::EncodingOptions(QCborValue::UseIntegers);
|
QTest::newRow("UseInteger:0") << QCborValue(0.) << raw("\x00") << QCborValue::EncodingOptions(QCborValue::UseIntegers);
|
||||||
|
@ -88,7 +88,9 @@ private slots:
|
|||||||
|
|
||||||
void testToFillPolygons();
|
void testToFillPolygons();
|
||||||
|
|
||||||
|
#if QT_CONFIG(signaling_nan)
|
||||||
void testNaNandInfinites();
|
void testNaNandInfinites();
|
||||||
|
#endif
|
||||||
|
|
||||||
void closing();
|
void closing();
|
||||||
|
|
||||||
@ -1228,6 +1230,7 @@ void tst_QPainterPath::testToFillPolygons()
|
|||||||
QCOMPARE(polygons.first().count(QPointF(70, 50)), 0);
|
QCOMPARE(polygons.first().count(QPointF(70, 50)), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if QT_CONFIG(signaling_nan)
|
||||||
void tst_QPainterPath::testNaNandInfinites()
|
void tst_QPainterPath::testNaNandInfinites()
|
||||||
{
|
{
|
||||||
QPainterPath path1;
|
QPainterPath path1;
|
||||||
@ -1271,6 +1274,7 @@ void tst_QPainterPath::testNaNandInfinites()
|
|||||||
path1.lineTo(QPointF(1, 1));
|
path1.lineTo(QPointF(1, 1));
|
||||||
QVERIFY(path1 != path2);
|
QVERIFY(path1 != path2);
|
||||||
}
|
}
|
||||||
|
#endif // signaling_nan
|
||||||
|
|
||||||
void tst_QPainterPath::connectPathDuplicatePoint()
|
void tst_QPainterPath::connectPathDuplicatePoint()
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user