问题
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;
}
回答1:
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++ addslong
andlong 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++ addsfloat
andlong 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).
回答2:
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|
.
回答3:
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
.
回答4:
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); }
来源:https://stackoverflow.com/questions/13460750/on-the-stdabs-function