# Generic absolute value function

I wanted to write a generic abs function that would correctly work for every type. Basically, I wanted to use the following algorithm:

• If the type is a built-in integer, use std::abs from <cstdlib>.
• If the type is a built-in floating-point, use std::abs from <cmath>.
• If the type is a user-defined type with a namespace-level abs, use it.
• Otherwise, call a generic abs algorithm.

Here is what I came up with:

#include <cmath>
#include <cstdlib>

namespace math
{
namespace detail
{
// generic abs algorithm
template<typename T>
constexpr auto abs(const T& value)
-> T
{
return (T{} < value) ? value : -value;
}
}

template<typename T>
constexpr auto abs(const T& value)
-> T
{
using std::abs;
using detail::abs;
return abs(value);
}
}


The idea is to create a generic detail::abs algorithm, then to create another abs function that will choose the function to call thanks to the argument-dependant lookup. Here is what I tried to take into account:

• While the generic algorithm also works for built-in integral and floating point types, std::abs may produce optimized code for these types. Using std::abs when possible will probably generate an optimized executable. That said, std::abs lacks constexpr, which is a desirable feature, and the compiler might recognize a absolute value-like construct and optimize it away...

• Some types have a namespace-level abs that does not behave like the generic algorithm. Therefore, we have to call this namespace-level function if it exist.

• Some types may be huge. Therefore, I chose to take the parameter by const& since some namespace-level abs may also take their parameter by const&.

• Some types only provide operator< to represent the ordering, but not the other relational operators. Therefore, calling operator< in the generic algorithm is more likely to work.

• I chose to use T{} instead of 0 for the comparison in the generic algorithm in order to be able to represent the default value for any given type. A type is not guaranteed to be comparable to an integer.

Here is a test case to demonstrate what the function can achieve (you can also test it online):

namespace eggs
{
struct Foo
{
Foo(int val):
val(val)
{}

int val;
};

Foo abs(Foo foo)
{
return { std::abs(foo.val) };
}
}

struct Bar
{
Bar(int val=0):
val(val)
{}

Bar operator-() const
{
return { -val };
}

int val;
};

bool operator<(const Bar& lhs, const Bar& rhs)
{
return lhs.val < rhs.val;
}

int main()
{
using namespace std::literals;

std::cout << math::abs(-5) << '\n';
std::cout << math::abs(-5.3f) << '\n';
std::cout << math::abs(-5i+2.0) << '\n';

eggs::Foo foo = { -8 };
std::cout << math::abs(foo).val << '\n';

Bar bar = { -9 };
std::cout << math::abs(bar).val << '\n';
}


What do you think of such a function? Did I miss any obscure error? Do you see anything that could be improved (in the implementation, I don't care about the test case)?

I believe you have a bug in your generic implementation, especially in relation to floating-point style classes that have signed-zero values:

return (T{} < value) ? value : -value;


would be better as a T{} <= value (or rewritten as (T{} > value) ? -value : value;).

Your current logic will return -0.0 for an input value of 0.0, and that's not appropriate for an abs() function.

Additionally, I don't know how you would really test these things, because, if I am not mistaken, in floating-point comparisons with signed-zero values, -0.0 == 0.0 yet I would expect that abs(-0.0) would return 0.0.

How you resolve this issue though, I don't know.

• That's actually a question: if it is not observable, does it matter? – Morwenn Aug 15 '14 at 16:04
• @Morwenn It's observable. To test, divide by zero! 1.0 / 0.0 is inf. 1.0 / -0.0 is -inf. – 200_success Aug 15 '14 at 16:42
• @200_success You're right. I'm not used to handling floating point issues. I almost never run into problems, so that's something that I generally don't even take into account (and that's a shame). That said, floating point values are handled by std::abs and not by the generic algorithm. Therefore, I don't think that I have a bug. – Morwenn Aug 15 '14 at 23:53
• @Morwenn - you will still be negating any value/class that presents as == to T{}. It would be safer to only negate those strictly less than T{} – rolfl Aug 15 '14 at 23:56
• Hm...good point. Traditionally, you only require < to be defined, so you'd want to switch the order of operands rather than the operator though. return a < T{} ? -a : a; – Jerry Coffin Aug 16 '14 at 4:32

The only point I'd make it to look into using boost::call_traits ( see here ). Instead of always passing by const T&, this will select the "best" way to pass a parameter: by const T for small, built in types (such as int), and by const T& for class types.

template<typename T>
constexpr auto abs(boost::call_traits<T>::param_type value)
-> T
{ ... }