21

std::abs()函数是否为 C++11 中的所有算术类型定义良好,并且返回|x|时不会出现近似问题?

奇怪的是,使用 g++4.7, std::abs(char), std::abs(short int), std::abs(int), std::abs(long int)andstd::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;
}
4

4 回答 4

16

保证正确的重载出现在<cmath>/中<cstdlib>

C++11,[c.math]:

除了int中某些数学函数的版本之外<cstdlib>,C++ 还添加longlong long重载了这些函数的版本,具有相同的语义。

添加的签名是:

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

[...]

除了double中的数学函数的版本之外<cmath>,这些函数的重载版本具有相同的语义。C++ 添加floatlong double重载了这些函数的版本,具有相同的语义。

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

因此,您应该确保正确包含<cstdlib>( int, long,long long重载)/ <cmath>( double, float,long double重载)。

于 2012-11-19T19:41:04.830 回答
5

您不能保证std::abs(x)始终返回|x|所有算术类型。例如,大多数有符号整数实现比正数多一个负数的空间,所以结果abs(numeric_limits<int>::min())将不等于|x|

于 2012-11-19T19:40:47.287 回答
1

检查您实际上使用的是std::absfrom<cstdlib>而不是std::absfrom <cmath>

PS。哦,刚刚看到示例程序,好吧,你去吧,你正在使用std::abs .

于 2012-11-19T19:36:21.013 回答
0

当您将std::absfrom<cmath>与整数类型一起使用时,g++(使用 C++11 标准)返回双精度并不奇怪:来自http://www.cplusplus.com/reference/cmath/abs/

自 C++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); }
于 2015-03-20T13:09:39.460 回答