[safe_op] Add Safe::abs(), calculates absoulte value without UB

Add a drop-in replacement for std::abs which never produces
negative values and thereby never invokes undefined behavior.
This commit is contained in:
Dan Čermák 2018-08-21 23:07:03 +02:00
parent 8b47a8efc1
commit 2dacb19933
2 changed files with 40 additions and 0 deletions

View File

@ -302,6 +302,37 @@ namespace Safe
return res;
}
/*!
* @brief Calculates the absolute value of a number without producing
* negative values.
*
* The "standard" implementation of `abs(num)` (`num < 0 ? -num : num`)
* produces negative values when `num` is the smallest negative number. This
* is caused by `-1 * INTMAX = INTMIN + 1`, i.e. the real result of
* `abs(INTMIN)` overflows the integer type and results in `INTMIN` again
* (this is not guaranteed as it invokes undefined behavior).
*
* This function does not exhibit this behavior, it returns
* `std::numeric_limits<T>::max()` when the input is
* `std::numeric_limits<T>::min()`. The downside of this is that two
* negative values produce the same absolute value:
* `std::numeric_limits<T>::min()` and `std::numeric_limits<T>::min() + 1`.
*
* @tparam T a signed integer type
* @param[in] num The number which absolute value should be computed.
* @throws Never throws an exception.
* @return The absolute value of `num` or `std::numeric_limits<T>::max()`
* when `num == std::numeric_limits<T>::min()`.
*/
template <typename T>
typename Internal::enable_if<Internal::is_signed<T>::VALUE, T>::type abs(T num) throw()
{
if (num == std::numeric_limits<T>::min()) {
return std::numeric_limits<T>::max();
}
return num < 0 ? -num : num;
}
} // namespace Safe
#endif // SAFE_OP_HPP_

View File

@ -181,3 +181,12 @@ TEST(safeAdd, checkSignedOverflow)
test_safe_add<long>();
test_safe_add<long long>();
}
TEST(safeAbs, checkValues)
{
static const int values[] = {-1, 1, std::numeric_limits<int>::max(), std::numeric_limits<int>::min() + 1};
for (size_t i = 0; i < sizeof(values) / sizeof(*values); ++i) {
ASSERT_EQ(Safe::abs(values[i]), abs(values[i]));
}
ASSERT_EQ(Safe::abs(std::numeric_limits<int>::min()), std::numeric_limits<int>::max());
}