Add qHypot() to qmath.h, exposing and extending std::hypot()
We have plenty of places where we add some squares and take a square root; this may be done more accurately and faster by hypot(). Introduce QHypotHelper to handle hypot with more than 3 parameters, and with 3 when the C++17 version is missing (which it never should be). Include an overload taking arbitrarily many valus and ensure that we can use qHypot() with qfloat16. Illustrate with some example uses, add some tests. [ChangeLog][QtCore][QMath] Header <QMath> now provides qHypot(), an implementation of std::hypot() taking arbitrarily many numeric values, including support for qfloat16, while avoiding the overflow and underflow problems that arise when naively taking the square root of a sum of squares. Change-Id: Ia4e3913fe83fc27d17d8e7f1a52f03ad445c1fed Reviewed-by: Andrei Golubev <andrei.golubev@qt.io> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
ad16f79e5f
commit
b08368d99f
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Copyright (C) 2021 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 by Southwest Research Institute (R)
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
@ -308,6 +308,63 @@ inline qfloat16::operator float() const noexcept
|
||||
return qAbs(static_cast<float>(f)) <= 0.001f;
|
||||
}
|
||||
|
||||
/*
|
||||
qHypot compatibility; see ../kernel/qmath.h
|
||||
*/
|
||||
namespace QtPrivate {
|
||||
template <typename R>
|
||||
struct QHypotType<R, qfloat16> { using type = decltype(std::hypot(R(1), 1.0f)); };
|
||||
template <typename R>
|
||||
struct QHypotType<qfloat16, R> { using type = decltype(std::hypot(1.0f, R(1))); };
|
||||
template <> struct QHypotType<qfloat16, qfloat16> { using type = qfloat16; };
|
||||
}
|
||||
// Avoid passing qfloat16 to std::hypot(), while ensuring return types
|
||||
// consistent with the above:
|
||||
template<typename F, typename ...Fs> auto qHypot(F first, Fs... rest);
|
||||
template <typename T, typename std::enable_if<!std::is_same<qfloat16, T>::value, int>::type = 0>
|
||||
auto qHypot(T x, qfloat16 y) { return qHypot(x, float(y)); }
|
||||
template <typename T, typename std::enable_if<!std::is_same<qfloat16, T>::value, int>::type = 0>
|
||||
auto qHypot(qfloat16 x, T y) { return qHypot(float(x), y); }
|
||||
template <> inline auto qHypot(qfloat16 x, qfloat16 y)
|
||||
{
|
||||
#if (defined(QT_COMPILER_SUPPORTS_F16C) && defined(__F16C__)) || defined (__ARM_FP16_FORMAT_IEEE)
|
||||
return QtPrivate::QHypotHelper<qfloat16>(x).add(y).result();
|
||||
#else
|
||||
return qfloat16(qHypot(float(x), float(y)));
|
||||
#endif
|
||||
}
|
||||
#if __cpp_lib_hypot >= 201603L // Expected to be true
|
||||
// If any are not qfloat16, convert each qfloat16 to float:
|
||||
/* (The following splits the some-but-not-all-qfloat16 cases up, using
|
||||
(X|Y|Z)&~(X&Y&Z) = X ? ~(Y&Z) : Y|Z = X&~(Y&Z) | ~X&Y | ~X&~Y&Z,
|
||||
into non-overlapping cases, to avoid ambiguity.) */
|
||||
template <typename Ty, typename Tz,
|
||||
typename std::enable_if<
|
||||
// Ty, Tz aren't both qfloat16:
|
||||
!(std::is_same_v<qfloat16, Ty> && std::is_same_v<qfloat16, Tz>), int>::type = 0>
|
||||
auto qHypot(qfloat16 x, Ty y, Tz z) { return qHypot(float(x), y, z); }
|
||||
template <typename Tx, typename Tz,
|
||||
typename std::enable_if<
|
||||
// Tx isn't qfloat16:
|
||||
!std::is_same_v<qfloat16, Tx>, int>::type = 0>
|
||||
auto qHypot(Tx x, qfloat16 y, Tz z) { return qHypot(x, float(y), z); }
|
||||
template <typename Tx, typename Ty,
|
||||
typename std::enable_if<
|
||||
// Neither Tx nor Ty is qfloat16:
|
||||
!std::is_same_v<qfloat16, Tx> && !std::is_same_v<qfloat16, Ty>, int>::type = 0>
|
||||
auto qHypot(Tx x, Ty y, qfloat16 z) { return qHypot(x, y, float(z)); }
|
||||
// If all are qfloat16, stay with qfloat16 (albeit via float, if no native support):
|
||||
template <>
|
||||
inline auto qHypot(qfloat16 x, qfloat16 y, qfloat16 z)
|
||||
{
|
||||
#if (defined(QT_COMPILER_SUPPORTS_F16C) && defined(__F16C__)) || defined (__ARM_FP16_FORMAT_IEEE)
|
||||
return QtPrivate::QHypotHelper<qfloat16>(x).add(y).add(z).result();
|
||||
#else
|
||||
return qfloat16(qHypot(float(x), float(y), float(z)));
|
||||
#endif
|
||||
}
|
||||
#endif // 3-arg std::hypot() is available
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(qfloat16)
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2021 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
@ -135,6 +135,72 @@ template <typename T> auto qSqrt(T v)
|
||||
return sqrt(v);
|
||||
}
|
||||
|
||||
namespace QtPrivate {
|
||||
template <typename R, typename F> // For qfloat16 to specialize
|
||||
struct QHypotType { using type = decltype(std::hypot(R(1), F(1))); };
|
||||
|
||||
// Implements hypot() without limiting number of arguments:
|
||||
template <typename T>
|
||||
class QHypotHelper
|
||||
{
|
||||
const T scale, total;
|
||||
template <typename F> friend class QHypotHelper;
|
||||
QHypotHelper(T first, T prior) : scale(first), total(prior) {}
|
||||
public:
|
||||
QHypotHelper(T first) : scale(qAbs(first)), total(1) {}
|
||||
T result() const
|
||||
{ return qIsFinite(scale) ? scale > 0 ? scale * T(std::sqrt(total)) : T(0) : scale; }
|
||||
|
||||
template<typename F, typename ...Fs>
|
||||
auto add(F first, Fs... rest) const
|
||||
{ return add(first).add(rest...); }
|
||||
|
||||
template<typename F, typename R = typename QHypotType<T, F>::type>
|
||||
QHypotHelper<R> add(F next) const
|
||||
{
|
||||
if (qIsInf(scale) || (qIsNaN(scale) && !qIsInf(next)))
|
||||
return QHypotHelper<R>(scale, R(1));
|
||||
if (qIsNaN(next))
|
||||
return QHypotHelper<R>(next, R(1));
|
||||
const R val = qAbs(next);
|
||||
if (!(scale > 0) || qIsInf(next))
|
||||
return QHypotHelper<R>(val, R(1));
|
||||
if (!(val > 0))
|
||||
return QHypotHelper<R>(scale, total);
|
||||
if (val > scale) {
|
||||
const R ratio = scale / next;
|
||||
return QHypotHelper<R>(val, total * ratio * ratio + 1);
|
||||
}
|
||||
const R ratio = next / scale;
|
||||
return QHypotHelper<R>(scale, total + ratio * ratio);
|
||||
}
|
||||
};
|
||||
} // QtPrivate
|
||||
|
||||
template<typename F, typename ...Fs>
|
||||
auto qHypot(F first, Fs... rest)
|
||||
{
|
||||
return QtPrivate::QHypotHelper<F>(first).add(rest...).result();
|
||||
}
|
||||
|
||||
// However, where possible, use the standard library implementations:
|
||||
template <typename Tx, typename Ty>
|
||||
auto qHypot(Tx x, Ty y)
|
||||
{
|
||||
// C99 has hypot(), hence C++11 has std::hypot()
|
||||
using std::hypot;
|
||||
return hypot(x, y);
|
||||
}
|
||||
|
||||
#if __cpp_lib_hypot >= 201603L // Expected to be true
|
||||
template <typename Tx, typename Ty, typename Tz>
|
||||
auto qHypot(Tx x, Ty y, Tz z)
|
||||
{
|
||||
using std::hypot;
|
||||
return hypot(x, y, z);
|
||||
}
|
||||
#endif // else: no need to over-ride the arbitrarily-many-arg form
|
||||
|
||||
template <typename T> auto qLn(T v)
|
||||
{
|
||||
using std::log;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the documentation of the Qt Toolkit.
|
||||
@ -139,7 +139,7 @@
|
||||
This function will return the angle (argument) of that point.
|
||||
|
||||
\relates <QtMath>
|
||||
\sa qAtan()
|
||||
\sa qAtan(), qHypot()
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -148,7 +148,57 @@
|
||||
This function returns a NaN if \a v is a negative number.
|
||||
|
||||
\relates <QtMath>
|
||||
\sa qPow()
|
||||
\sa qPow(), qHypot()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\since 6.1
|
||||
\overload
|
||||
\fn template <typename Tx, typename Ty> auto qHypot(Tx x, Ty y)
|
||||
Returns the distance of a point (x, y) from the origin (0, 0).
|
||||
|
||||
This is qSqrt(x * x + y * y), optimized.
|
||||
In particular, underflow and overflow may be avoided.
|
||||
|
||||
Accepts any mix of numeric types, returning the same
|
||||
floating-point type as std::hypot(). If either parameter is
|
||||
infinite, so is the result; otherwise, if either is a NaN, so is
|
||||
the result.
|
||||
|
||||
\relates <QtMath>
|
||||
\sa qSqrt(), qAtan2()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\since 6.1
|
||||
\overload
|
||||
\fn template <typename Tx, typename Ty, typename Tz> auto qHypot(Tx x, Ty y, Tz z)
|
||||
Returns the distance of a point (x, y, z) from the origin (0, 0, 0).
|
||||
|
||||
This is qSqrt(x * x + y * y + z * z), optimized where supported.
|
||||
In particular, underflow and overflow may be avoided.
|
||||
|
||||
Accepts any mix of numeric types, returning the same
|
||||
floating-point type as std::hypot(). If any parameter is infinite,
|
||||
so is the result; otherwise, if any is NaN, so is the result.
|
||||
|
||||
\relates <QtMath>
|
||||
\sa qSqrt()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\since 6.1
|
||||
\fn template<typename F, typename ...Fs> auto qHypot(F first, Fs... rest)
|
||||
Returns the distance from origin in arbitrarily many dimensions
|
||||
|
||||
This is as for the two-argument and three-argument forms, supported by
|
||||
std::hypot(), but with as many numeric parameters as you care to pass to
|
||||
it. Uses \a first and each of the \a rest as co-ordinates, performing a
|
||||
calculation equivalent to squaring each, summing and returning the square
|
||||
root, save that underflow and overflow are avoided as far as possible.
|
||||
|
||||
\relates <QtMath>
|
||||
\sa qSqrt()
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
@ -572,8 +572,7 @@ QDataStream &operator>>(QDataStream &stream, QLine &line)
|
||||
*/
|
||||
qreal QLineF::length() const
|
||||
{
|
||||
using std::hypot;
|
||||
return hypot(dx(), dy());
|
||||
return qHypot(dx(), dy());
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -651,12 +650,11 @@ QLineF QLineF::fromPolar(qreal length, qreal angle)
|
||||
*/
|
||||
QLineF QLineF::unitVector() const
|
||||
{
|
||||
qreal x = dx();
|
||||
qreal y = dy();
|
||||
using std::hypot;
|
||||
qreal len = hypot(x, y);
|
||||
const qreal x = dx();
|
||||
const qreal y = dy();
|
||||
|
||||
QLineF f(p1(), QPointF(pt1.x() + x/len, pt1.y() + y/len));
|
||||
const qreal len = qHypot(x, y);
|
||||
QLineF f(p1(), QPointF(pt1.x() + x / len, pt1.y() + y / len));
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
if (qAbs(f.length() - 1) >= 0.001)
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
@ -51,6 +51,7 @@
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/qmath.h>
|
||||
#include <QtGui/private/qtguiglobal_p.h>
|
||||
#include <private/qdatabuffer_p.h>
|
||||
#include <qvarlengtharray.h>
|
||||
@ -134,18 +135,9 @@ private:
|
||||
inline void QTriangulatingStroker::normalVector(float x1, float y1, float x2, float y2,
|
||||
float *nx, float *ny)
|
||||
{
|
||||
float dx = x2 - x1;
|
||||
float dy = y2 - y1;
|
||||
Q_ASSERT(dx != 0 || dy != 0);
|
||||
|
||||
float pw;
|
||||
|
||||
if (dx == 0)
|
||||
pw = m_width / std::abs(dy);
|
||||
else if (dy == 0)
|
||||
pw = m_width / std::abs(dx);
|
||||
else
|
||||
pw = m_width / std::sqrt(dx*dx + dy*dy);
|
||||
const float dx = x2 - x1;
|
||||
const float dy = y2 - y1;
|
||||
const float pw = m_width / qHypot(dx, dy);
|
||||
|
||||
*nx = -dy * pw;
|
||||
*ny = dx * pw;
|
||||
|
@ -43,11 +43,11 @@
|
||||
#include "qxcbscreen.h"
|
||||
#include "qxcbwindow.h"
|
||||
#include "QtCore/qmetaobject.h"
|
||||
#include "QtCore/qmath.h"
|
||||
#include <QtGui/qpointingdevice.h>
|
||||
#include <QtGui/private/qpointingdevice_p.h>
|
||||
#include <qpa/qwindowsysteminterface_p.h>
|
||||
#include <QDebug>
|
||||
#include <cmath>
|
||||
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
@ -758,11 +758,11 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
|
||||
} else if (vci.label == QXcbAtom::AbsMTTouchMajor) {
|
||||
const qreal sw = screen->geometry().width();
|
||||
const qreal sh = screen->geometry().height();
|
||||
w = valuatorNormalized * std::sqrt(sw * sw + sh * sh);
|
||||
w = valuatorNormalized * qHypot(sw, sh);
|
||||
} else if (vci.label == QXcbAtom::AbsMTTouchMinor) {
|
||||
const qreal sw = screen->geometry().width();
|
||||
const qreal sh = screen->geometry().height();
|
||||
h = valuatorNormalized * std::sqrt(sw * sw + sh * sh);
|
||||
h = valuatorNormalized * qHypot(sw, sh);
|
||||
} else if (vci.label == QXcbAtom::AbsMTOrientation) {
|
||||
// Find the closest axis.
|
||||
// 0 corresponds to the Y axis, vci.max to the X axis.
|
||||
|
@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2021 The Qt Company Ltd.
|
||||
** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
@ -29,6 +29,7 @@
|
||||
|
||||
#include <QTest>
|
||||
#include <qmath.h>
|
||||
#include <qfloat16.h>
|
||||
|
||||
class tst_QMath : public QObject
|
||||
{
|
||||
@ -41,6 +42,7 @@ private slots:
|
||||
void radiansToDegrees();
|
||||
void trigonometry_data();
|
||||
void trigonometry();
|
||||
void hypotenuse();
|
||||
void funcs_data();
|
||||
void funcs();
|
||||
void qNextPowerOfTwo32S_data();
|
||||
@ -152,7 +154,7 @@ void tst_QMath::trigonometry()
|
||||
QFETCH(const double, x);
|
||||
QFETCH(const double, y);
|
||||
QFETCH(const double, angle);
|
||||
const double hypot = std::hypot(x, y);
|
||||
const double hypot = qHypot(x, y);
|
||||
QVERIFY(hypot > 0);
|
||||
QCOMPARE(qAtan2(y, x), angle);
|
||||
QCOMPARE(qSin(angle), y / hypot);
|
||||
@ -167,6 +169,79 @@ void tst_QMath::trigonometry()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMath::hypotenuse()
|
||||
{
|
||||
// Correct return-types, particularly when qfloat16 is involved:
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(1), qfloat16(1), qfloat16(1),
|
||||
qfloat16(1), qfloat16(1), qfloat16(1),
|
||||
qfloat16(1), qfloat16(1), qfloat16(1))),
|
||||
qfloat16>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), qfloat16(4), qfloat16(12))),
|
||||
qfloat16>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), qfloat16(4), 12.0f)), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0f, qfloat16(12))), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0f, qfloat16(4), qfloat16(12))), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0f, 12.0f)), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0f, qfloat16(4), 12.0f)), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0f, 4.0f, qfloat16(12))), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), qfloat16(4))), qfloat16>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0f, qfloat16(4))), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0f)), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0, qfloat16(4))), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3, qfloat16(4))), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0L)), long double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0L, qfloat16(4))), long double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0f, 4.0f)), float>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0f, 4.0)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0f, 4)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0f, 4.0L)), long double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0, 4.0f)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3, 4.0f)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0L, 4.0f)), long double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0, 4.0L)), long double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0L, 4.0)), long double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0, 4.0)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3, 4.0)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3.0, 4)), double>::value);
|
||||
static_assert(std::is_same<decltype(qHypot(3, 4)), double>::value);
|
||||
|
||||
// Works for all numeric types:
|
||||
QCOMPARE(qHypot(3, 4), 5);
|
||||
QCOMPARE(qHypot(qfloat16(5), qfloat16(12)), qfloat16(13));
|
||||
QCOMPARE(qHypot(3.0f, 4.0f, 12.0f), 13.0f);
|
||||
QCOMPARE(qHypot(3.0, 4.0, 12.0, 84.0), 85.0);
|
||||
QCOMPARE(qHypot(3.0f, 4.0f, 12.0f, 84.0f, 720.0f), 725.0f);
|
||||
QCOMPARE(qHypot(3.0, 4.0, 12.0, 84.0, 3612.0), 3613.0);
|
||||
// Integral gets promoted to double:
|
||||
QCOMPARE(qHypot(1, 1), M_SQRT2);
|
||||
// Caller can mix types freely:
|
||||
QCOMPARE(qHypot(3.0f, 4, 12.0, 84.0f, qfloat16(720), 10500), 10525);
|
||||
// NaN wins over any finite:
|
||||
QCOMPARE(qHypot(3, 4.0, 12.0f, qQNaN()), qQNaN());
|
||||
QCOMPARE(qHypot(3, 4.0, qQNaN(), 12.0f), qQNaN());
|
||||
QCOMPARE(qHypot(3, qQNaN(), 4.0, 12.0f), qQNaN());
|
||||
QCOMPARE(qHypot(qQNaN(), 3, 4.0, 12.0f), qQNaN());
|
||||
// but Infinity beats NaN:
|
||||
QCOMPARE(qHypot(3, 4.0f, -qInf(), qQNaN()), qInf());
|
||||
QCOMPARE(qHypot(3, -qInf(), 4.0f, qQNaN()), qInf());
|
||||
QCOMPARE(qHypot(-qInf(), 3, 4.0f, qQNaN()), qInf());
|
||||
QCOMPARE(qHypot(qQNaN(), 3, -qInf(), 4.0f), qInf());
|
||||
QCOMPARE(qHypot(3, qQNaN(), 4.0f, -qInf()), qInf());
|
||||
QCOMPARE(qHypot(3, 4.0f, qQNaN(), -qInf()), qInf());
|
||||
// Components whose squares sum to zero don't change the end result:
|
||||
const double minD = std::numeric_limits<double>::min();
|
||||
QVERIFY(minD * minD + minD * minD == 0); // *NOT* QCOMPARE
|
||||
QCOMPARE(qHypot(minD, minD, 12.0), 12.0);
|
||||
const float minF = std::numeric_limits<float>::min();
|
||||
QVERIFY(minF * minF + minF * minF == 0.0f); // *NOT* QCOMPARE
|
||||
QCOMPARE(qHypot(minF, minF, 12.0f), 12.0f);
|
||||
const qfloat16 minF16 = std::numeric_limits<qfloat16>::min();
|
||||
QVERIFY(minF16 * minF16 + minF16 * minF16 == qfloat16(0)); // *NOT* QCOMPARE
|
||||
QCOMPARE(qHypot(minF16, minF16, qfloat16(12)), qfloat16(12));
|
||||
}
|
||||
|
||||
void tst_QMath::funcs_data()
|
||||
{
|
||||
QTest::addColumn<double>("value");
|
||||
|
Loading…
Reference in New Issue
Block a user