О функции std :: abs

Это std::abs() функция хорошо определена для ВСЕХ арифметических типов в C ++ 11 и будет возвращать |x| без проблем аппроксимации?

Странная вещь в том, что с g ++ 4.7, std::abs(char), std::abs(short int), std::abs(int), std::abs(long int) а также std::abs(long long int) кажется, вернуть двойной (напротив: http://en.cppreference.com/w/cpp/numeric/math/abs). И если число приведено к двойному, мы могли бы иметь некоторую ошибку аппроксимации для очень большого числа (например, -9223372036854775806LL = 2^63-3).

Так у меня есть гарантия, что std::abs(x) всегда вернется |x| для всех арифметических типов?

РЕДАКТИРОВАТЬ: вот пример программы, чтобы сделать несколько тестов

#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;
}

20

Решение

Правильные перегрузки гарантированно присутствуют в <cmath>/<cstdlib>:

C ++ 11, [c.math]:

В добавок к int версии некоторых математических функций в <cstdlib>, C ++ добавляет long а также long long перегруженные версии этих функций, с одинаковой семантикой.

Добавленные подписи:

long abs(long);            // labs()
long long abs(long long);  // llabs()
[…]

В добавок к double версии математических функций в <cmath>перегруженные версии этих функций, с одинаковой семантикой.
C ++ добавляет float а также long double перегруженные версии этих функций, с одинаковой семантикой.

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

Таким образом, вы должны просто убедиться, что правильно <cstdlib> (int, long, long long Перегрузки) /<cmath> (double, float, long double Перегрузки).

16

Другие решения

Вы не можете гарантировать, что std::abs(x) всегда вернется |x| для всех арифметических типов. Например, в большинстве целочисленных реализаций со знаком есть место для еще одного отрицательного числа, чем положительного числа, поэтому результаты abs(numeric_limits<int>::min()) не будет равных |x|,

4

Проверьте, что вы на самом деле используете std::abs от <cstdlib> и не std::abs от <cmath>,

PS. О, только что видел пример программы, ну, вы идете, вы используете одну из перегрузок с плавающей запятой std::abs
,

1

Не странно, что g ++ (со стандартом C ++ 11) возвращает double, когда вы используете std::abs от <cmath> с целочисленным типом:
От http://www.cplusplus.com/reference/cmath/abs/:

Начиная с C ++ 11, в этом заголовке предусмотрены дополнительные перегрузки (<cmath>) для целочисленных типов: эти перегрузки эффективно приводят x к двойному перед вычислениями (определено для T, являющегося любым целочисленным типом).

Это на самом деле реализовано в /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); }
0
По вопросам рекламы [email protected]