diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2020-09-09 14:59:32 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2020-09-30 16:26:49 +0200 |
commit | 1c591fd9246ca776304a3c370dd2578bd886feac (patch) | |
tree | 02a46d7f631832955487de2fd5c5b5856dfbcfad /src/corelib/tools/qline.h | |
parent | c25687fa0b6e4be043e1f8c92c093d8b06ca06c4 (diff) |
Deal with {und,ov}erflow issues in QLine's length handling
Use std::hypot() instead of sqrt() of a sum of squares.
This ensures length() can't be zero when isNull() is false.
Use length() in QLine::setLength() rather than duplicating that.
Clarify and expand some documentation; isNull() never said what
constituted validity, nor did unitVector() mention that is should not
be used on a line for which isNull() is true. Make clear that lines of
denormal length cannot be rescaled accurately.
Given that we use fuzzy comparison to determine equality of
end-points, isNull() can be false for a line with displacements less
than sqrt(numeric_limits<qreal>::denorm_min()) between the coordinates
of its end-points (as long as these are not much bigger); squaring
these would give zero, hence a zero length, where using hypot() avoids
the underflow and gives a non-zero length. Having a zero length for a
line with isNull() false would lead to problems in setLength(), which
uses an isNull() pre-test, protecting a call to unitVector().
(It was already possible for a null line to have non-zero length; this
now arises in more cases.)
Restored QLine::setLength() to the form it had before a recent change
to avoid division by zero (which resulted from underflow in computing
the length of a non-null line) but allow for the possibility that the
unit vector it computes as transient may not have length exactly one.
Add tests against {ov,und}erflow problems in QLine. Reworked the test
added during the divide-by-zero fix to make it part of the existing
test.
Pick-to: 5.15 5.12
Change-Id: I7b71d66b872ccc08a64e941acd36b45b0ea15fab
Reviewed-by: Sze Howe Koh <szehowe.koh@gmail.com>
Diffstat (limited to 'src/corelib/tools/qline.h')
-rw-r--r-- | src/corelib/tools/qline.h | 13 |
1 files changed, 5 insertions, 8 deletions
diff --git a/src/corelib/tools/qline.h b/src/corelib/tools/qline.h index adef545d86..8d57e09c4a 100644 --- a/src/corelib/tools/qline.h +++ b/src/corelib/tools/qline.h @@ -40,8 +40,6 @@ #ifndef QLINE_H #define QLINE_H -#include <QtCore/qmath.h> - #include <QtCore/qpoint.h> QT_BEGIN_NAMESPACE @@ -372,15 +370,14 @@ constexpr inline QPointF QLineF::center() const return QPointF(0.5 * pt1.x() + 0.5 * pt2.x(), 0.5 * pt1.y() + 0.5 * pt2.y()); } -QT_WARNING_DISABLE_FLOAT_COMPARE - inline void QLineF::setLength(qreal len) { - const qreal oldlength = qSqrt(dx() * dx() + dy() * dy()); - if (!oldlength) + if (isNull()) return; - const qreal factor = len / oldlength; - pt2 = QPointF(pt1.x() + dx() * factor, pt1.y() + dy() * factor); + Q_ASSERT(length() > 0); + const QLineF v = unitVector(); + len /= v.length(); // In case it's not quite exactly 1. + pt2 = QPointF(pt1.x() + len * v.dx(), pt1.y() + len * v.dy()); } constexpr inline QPointF QLineF::pointAt(qreal t) const |