Refine QRoundingDown::qDiv() to avoid underflow

Subtracting denominator - 1 from the numerator ran into trouble if the
numerator was close to its type's minimum representable value. Adding
1 before division, then subtracting it after, avoids that underflow
while getting the same answer.

Change-Id: I0bb85deaa2ad36f8744ed6dbfdb4817442dddebe
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2022-05-03 15:59:48 +02:00
parent 4222d117d2
commit 93125d3a05

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -62,10 +62,17 @@ namespace QRoundingDown {
From C++11 onwards, integer division is defined to round towards zero, so we
can rely on that when implementing this. This is only used with denominator b
> 0, so we only have to treat negative numerator, a, specially.
If a is a multiple of b, adding 1 before and subtracting it after dividing by
b gets us to where we should be (albeit by an eccentric path), since the
adding caused rounding up, undone by the subtracting. Otherwise, adding 1
doesn't change the result of dividing by b; and we want one less than that
result. This is equivalent to subtracting b - 1 and simply dividing, except
when that subtraction would underflow.
*/
template<typename Int> constexpr Int qDiv(Int a, unsigned b)
{ return (a - (a < 0 ? int(b - 1) : 0)) / int(b); }
{ return a < 0 ? (a + 1) / int(b) - 1 : a / int(b); }
template<typename Int> constexpr Int qMod(Int a, unsigned b)
{ return a - qDiv(a, b) * b; }