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

Является ли функция std::abs() корректно определена для ВСЕХ арифметических типов в С++ 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). И если число заносится в double, мы могли бы иметь некоторую ошибку приближения для очень большого числа (например, -9223372036854775806LL = 2^63-3).

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

EDIT: вот примерная программа для выполнения некоторых тестов

#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

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

С++ 11, [c.math]:

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

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

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

[...]

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

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

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

Ответ 2

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

Ответ 3

Убедитесь, что вы фактически используете std::abs от <cstdlib>, а не std::abs от <cmath>.

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

Ответ 4

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

Так как С++ 11, в этом заголовке (<cmath>) предусмотрены дополнительные перегрузки для интегральных типов: эти перегрузки эффективно передают x в double перед вычислениями (определены для того, чтобы 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); }