Skip to content

Commit 20bb598

Browse files
committed
type_max_num_digits: correctly handle signed types
In type_max_num_digits() properly handle signed types that require more digits to represent the minimum value than they do the maximum value. A signed type often has a minimum value that has a larger absolute value than the maximum value. In theory, because of this, the minimum value could require more digits to represent. (In practice this doesn't really occur; minimum/maximum values tend to have the same number of decimal digits. However, this change makes the code correct even in those theoretical cases).
1 parent 28d99c3 commit 20bb598

File tree

1 file changed

+25
-2
lines changed

1 file changed

+25
-2
lines changed

src/includes/dinit-util.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <algorithm>
99
#include <stdexcept>
1010
#include <limits>
11+
#include <type_traits>
1112

1213
#include <cstring>
1314
#include <cstddef>
@@ -498,12 +499,34 @@ inline bool starts_with(const std::string &s, const char *prefix) noexcept
498499
return *prefix == 0;
499500
}
500501

502+
// Numeric maximum as constexpr (not needed in C++14 which has constexpr std::max).
503+
template <typename T> constexpr
504+
T constexpr_max(T a, T b)
505+
{
506+
return (a > b) ? a : b;
507+
}
508+
501509
// Calculate (at compile-time) the maximum number of decimal digits that can be in a value of a
502510
// specific numeric type (not including minus sign, trailing nul terminator).
503-
template <typename T> constexpr unsigned type_max_num_digits(T num = std::numeric_limits<T>::max(),
511+
template <typename T> constexpr
512+
typename std::enable_if<std::is_unsigned<T>::value, unsigned>::type
513+
type_max_num_digits(T num = std::numeric_limits<T>::max(),
504514
unsigned pow = 0)
505515
{
506-
return (num == 0) ? pow : type_max_num_digits(num / 10, pow + 1);
516+
return (num == 0) ? pow : type_max_num_digits<T>(num / 10, pow + 1);
517+
}
518+
519+
// Specialise type_max_num_digits for signed types. Since the (absolute value of the) minimum can
520+
// be larger than the maximum, account for that before counting digits.
521+
template <typename T> constexpr
522+
typename std::enable_if<std::is_signed<T>::value, unsigned>::type
523+
type_max_num_digits()
524+
{
525+
// For signed types, the absolute value of the minimum representable value may be larger than
526+
// the maximum representable value. Base the digit calculation on whichever is larger.
527+
using T_u = typename std::make_unsigned<T>::type;
528+
return type_max_num_digits<T_u>(constexpr_max((T_u)std::numeric_limits<T>::max(),
529+
(T_u)(-(T_u)std::numeric_limits<T>::min())), 0);
507530
}
508531

509532
// An allocator that doesn't value-initialise for construction. Eg for containers of primitive types this

0 commit comments

Comments
 (0)