21

Is the std::abs() function well defined for ALL arithmetic types in C++11 and will return |x| with no problem of approximation?

A weird thing is that with g++4.7, std::abs(char), std::abs(short int), std::abs(int), std::abs(long int) and std::abs(long long int) seem to return a double (on the contrary of : http://en.cppreference.com/w/cpp/numeric/math/abs). And if the number is casted to a double, we could have some approximation error for very large number (like -9223372036854775806LL = 2^63-3).

So do I have the guarantee that std::abs(x) will always return |x| for all arithmetic types ?

EDIT : here is an example program to make some tests

#include <iostream>
#include <iomanip>
#include <cmath>
#include <typeinfo>

template<typename T>
void abstest(T x)
{
    static const unsigned int width = 16;
    const T val = x;
    if (sizeof(val) == 1) {
        std::cout<<std::setw(width)<<static_cast<int>(val)<<" ";
        std::cout<<std::setw(width)<<static_cast<int>(std::abs(val))<<" ";
    } else {
        std::cout<<std::setw(width)<<val<<" ";
        std::cout<<std::setw(width)<<static_cast<T>(std::abs(val))<<" ";
    }
    std::cout<<std::setw(width)<<sizeof(val)<<" ";
    std::cout<<std::setw(width)<<sizeof(std::abs(val))<<" ";
    std::cout<<std::setw(width)<<typeid(val).name()<<" ";
    std::cout<<std::setw(width)<<typeid(std::abs(val)).name()<<std::endl;
}

int main()
{
    double ref = -100000000000;
    abstest<char>(ref);
    abstest<short int>(ref);
    abstest<int>(ref);
    abstest<long int>(ref);
    abstest<long long int>(ref);
    abstest<signed char>(ref);
    abstest<signed short int>(ref);
    abstest<signed int>(ref);
    abstest<signed long int>(ref);
    abstest<signed long long int>(ref);
    abstest<unsigned char>(ref);
    abstest<unsigned short int>(ref);
    abstest<unsigned int>(ref);
    abstest<unsigned long int>(ref);
    abstest<unsigned long long int>(ref);
    abstest<float>(ref);
    abstest<double>(ref);
    abstest<long double>(ref);
    return 0;
}
ildjarn
  • 62,044
  • 9
  • 127
  • 211
Vincent
  • 57,703
  • 61
  • 205
  • 388
  • 3
    What makes you think the g++ implementation is returning a double? Perhaps you could provide a sample of what you're doing that indicates a double is being returned? –  Nov 19 '12 at 19:33
  • Note that there are several `std::abs` in different headers, like `` and ``. – Bo Persson Nov 19 '12 at 19:35
  • 1
    Of course `std::abs(x)` returns `|x|`. Perhaps you're wondering if `decltype(std::abs(x))` will match `decltype(x)`? I'm just a little confused by exactly what you mean by "will std::abs(x) always return |x|?" – Cornstalks Nov 19 '12 at 19:36
  • 2
    I don't know what the C++ standard says, but such a guarantee is certainly impossible, since whenever `int` is a two's-complement signed integer, the absolute value of the minimum possible `int` is not representable as an `int`. (For example, if we have 32-bit integers, then the minimum possible value is -2,147,483,648, but the maximum possible value is only 2,147,483,647.) – ruakh Nov 19 '12 at 19:38
  • I don't know about your gcc 4.7, but my gcc 4.7 calls `__gnu_cxx::abs`, which is an inline wrapper around `__x >= 0 ? __x : -__x;` – Damon Nov 19 '12 at 19:41
  • Could you post the output for your program? I don't have gcc 4.7. – Cornstalks Nov 19 '12 at 19:42
  • Also see [Is std::abs(0u) ill-formed?](http://stackoverflow.com/q/29750946/1708801) – Shafik Yaghmour Jun 03 '15 at 15:39

4 Answers4

16

The correct overloads are guaranteed to be present in <cmath>/<cstdlib>:

C++11, [c.math]:

In addition to the int versions of certain math functions in <cstdlib>, C++ adds long and long long overloaded versions of these functions, with the same semantics.

The added signatures are:

long abs(long);            // labs()
long long abs(long long);  // llabs()

[...]

In addition to the double versions of the math functions in <cmath>, overloaded versions of these functions, with the same semantics. C++ adds float and long double overloaded versions of these functions, with the same semantics.

float abs(float);
long double abs(long double);

So you should just make sure to include correctly <cstdlib> (int, long, long long overloads)/<cmath> (double, float, long double overloads).

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
5

You cannot guarantee that std::abs(x) will always return |x| for all arithmetic types. For example, most signed integer implementations have room for one more negative number than positive number, so the results of abs(numeric_limits<int>::min()) will not equal |x|.

Robert Cooper
  • 1,270
  • 9
  • 11
1

Check that you're in fact using std::abs from <cstdlib> and not std::abs from <cmath>.

PS. Oh, just saw the example program, well, there you go, you are using one of the floating point overloads of std::abs .

chill
  • 16,470
  • 2
  • 40
  • 44
0

It's not weird that g++ (with C++11 standard) returns a double when you use std::abs from <cmath> with an integral type: From http://www.cplusplus.com/reference/cmath/abs/:

Since C++11, additional overloads are provided in this header (<cmath>) for the integral types: These overloads effectively cast x to a double before calculations (defined for T being any integral type).

This is actually implemented like that in /usr/include/c++/cmath:

template<typename _Tp>
inline _GLIBCXX_CONSTEXPR
typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
                                double>::__type
abs(_Tp __x)
{ return __builtin_fabs(__x); }
user1448926
  • 399
  • 2
  • 7