[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:
parent
8b47a8efc1
commit
2dacb19933
@ -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_
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user