2012-11-19 73 views
19

La funzione std::abs() è ben definita per TUTTI i tipi aritmetici in C++ 11 e restituirà |x| senza alcun problema di approssimazione?Sulla funzione std :: abs

Una cosa strana è che con g ++ 4.7, std::abs(char), std::abs(short int), std::abs(int), std::abs(long int) e std::abs(long long int) sembrano restituire un doppio (al contrario di: http://en.cppreference.com/w/cpp/numeric/math/abs). E se il numero è castato a un doppio, potremmo avere un errore di approssimazione per un numero molto grande (come -9223372036854775806LL = 2^63-3).

Così ho la garanzia che restituirà sempre |x| per tutti i tipi di aritmetica?

EDIT: qui è un programma di esempio per fare delle prove

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

cosa ti fa pensare il g ++ implementazione sta tornando un doppio? Forse potresti fornire un esempio di ciò che stai facendo che indica che viene restituito un doppio? –

+0

Si noti che ci sono diversi 'std :: abs' in diverse intestazioni, come' 'e' '. –

+1

Ovviamente 'std :: abs (x)' restituisce '| x |'. Forse ti starai chiedendo se 'decltype (std :: abs (x))' corrisponderà a decltype (x) '? Sono solo un po 'confuso da cosa intendi esattamente per "sarà std :: abs (x) restituire sempre | x |?" – Cornstalks

risposta

15

Il sovraccarico corretti sono garantiti per essere presente in <cmath>/<cstdlib>:

C++ 11 [c.math]:

Oltre alle int versioni di alcune funzioni matematiche in <cstdlib> , C++ aggiunge long e long long versioni sovraccariche di queste funzioni, con la stessa semantica.

Le firme sono aggiunti:

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

[...]

Oltre ai double versioni delle funzioni matematiche in <cmath>, versioni di overload di queste funzioni, con la stessa semantica. C++ aggiunge float e long double versioni sovraccariche di queste funzioni, con la stessa semantica.

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

Quindi, si deve solo fare in modo di includere correttamente <cstdlib> (int, long, long long sovraccarichi)/<cmath> (double, float, long double sovraccarichi).

+6

Sezione 26.8.7-9 nel caso qualcuno se ne preoccupi. – WhozCraig

+1

perché i sovraccarichi sono separati? perché non metterli tutti in un unico file di intestazione? –

+0

Seguono la posizione delle corrispondenti funzioni C ('abs' e' fabs'), che non sono sicuro che fosse una buona idea, poiché più di una volta mi è capitato di non importare '' e quindi di usare intero - abs rispetto ai valori FP. –

1

Controlla che sei in realtà utilizzando std::abs da <cstdlib> e non std::abs da <cmath>.

PS. Oh, ho appena visto il programma di esempio, beh, ci sei, stai usando uno dei sovraccarichi in virgola mobile di std::abs .

+0

In realtà il suo codice di esempio _lyly_ utilizza 'cmath'. –

4

Non è possibile garantire che restituisca sempre per tutti i tipi di aritmetica. Ad esempio, la maggior parte delle implementazioni di interi con segno ha spazio per un numero negativo in più rispetto al numero positivo, pertanto i risultati di abs(numeric_limits<int>::min()) non saranno uguali a |x|.

+0

Sembra l'unico caso, sicuro per 'NaN'. – Kos

+1

Ed è il caso che l'OP colpisca, convertendo il doppio -100000000000 in altri tipi, troncando il valore al limite minimo in molti casi. – nos

0

Non è strano che g ++ (con C++ 11 standard) restituisce un doppio quando si utilizza std::abs da <cmath> con un tipo integrale: Da http://www.cplusplus.com/reference/cmath/abs/:

Poiché C++ 11, sovraccarichi aggiuntivi sono fornito in questa intestazione (<cmath>) per i tipi integrali: Questi overload eseguono efficacemente il cast x su un valore double prima dei calcoli (definito per T come qualsiasi tipo integrale).

questo è in realtà implementato come quella 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); }