web-dev-qa-db-ja.com

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 )。また、数値がdoubleにキャストされた場合、非常に大きな数値(_-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;
}
_
21
Vincent

正しいオーバーロードが<cmath>/<cstdlib>に存在することが保証されています:

C++ 11、[c.math]:

<cstdlib>の特定の数学関数のintバージョンに加えて、C++は同じセマンティクスでこれらの関数のlongおよびlong longオーバーロードバージョンを追加します。

追加された署名は次のとおりです。

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

[...]

<cmath>の数学関数のdoubleバージョンに加えて、これらの関数のオーバーロードバージョンが同じセマンティクスで使用されます。 C++は、これらの関数のfloatおよびlong doubleオーバーロードバージョンを同じセマンティクスで追加します。

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

したがって、<cstdlib>intlonglong long overloads)/ <cmath>doublefloatlong doubleオーバーロード)。

16
Matteo Italia

すべての算術型について、std::abs(x)が常に_|x|_を返すことを保証することはできません。たとえば、ほとんどの符号付き整数の実装には、正の数よりも1つ多い負の数の余地があるため、abs(numeric_limits<int>::min())の結果は_|x|_とは異なります。

4
Robert Cooper

std::abs<cstdlib>ではなく、実際にstd::abs<cmath>を使用していることを確認してください。

PS。ああ、サンプルプログラムを見たところです。さて、これで、std::absの浮動小数点オーバーロードの1つを使用しています。

1
chill

整数型でstd::absから<cmath>を使用すると、g ++(C++ 11標準を使用)がdoubleを返すことは奇妙ではありません。From 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); }
0
user1448926