2012-11-19 36 views
19

Chức năng std::abs() có được xác định rõ ràng cho TẤT CẢ các loại số học trong C++ 11 và sẽ trả về |x| mà không có vấn đề về xấp xỉ?Trên std :: abs function

Một điều kỳ lạ là với g ++ 4.7, std::abs(char), std::abs(short int), std::abs(int), std::abs(long int)std::abs(long long int) dường như trở lại một đôi (trên trái của: http://en.cppreference.com/w/cpp/numeric/math/abs). Và nếu số được đúc thành một đôi, chúng tôi có thể có một số lỗi xấp xỉ cho số lượng rất lớn (như -9223372036854775806LL = 2^63-3).

Vì vậy, tôi có đảm bảo rằng std::abs(x) sẽ luôn trả lại |x| cho tất cả các loại số học không?

EDIT: đây là một chương trình ví dụ để làm một số xét nghiệm

#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

làm cho bạn nghĩ gì thi g ++ được trả lại một đôi? Có lẽ bạn có thể cung cấp một mẫu của những gì bạn đang làm cho thấy một đôi đang được trả lại? –

+0

Lưu ý rằng có một số 'std :: abs' trong các tiêu đề khác nhau, như' 'và' '. –

+1

Tất nhiên 'std :: abs (x)' trả về '| x |'. Có lẽ bạn đang tự hỏi nếu 'decltype (std :: abs (x))' sẽ khớp 'decltype (x)'? Tôi chỉ là một chút bối rối bởi chính xác những gì bạn có nghĩa là bởi "sẽ std :: abs (x) luôn luôn trở lại | x |?" – Cornstalks

Trả lời

15

Các quá tải đúng được đảm bảo để có mặt trong <cmath>/<cstdlib>:

C++ 11, [c.math]:

Ngoài các int phiên bản của hàm toán học nhất định trong <cstdlib> , C++ thêm longlong long các phiên bản quá tải của các hàm này, với cùng ngữ nghĩa.

Chữ ký bổ sung là:

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

[...]

Ngoài các double phiên bản của các hàm toán học trong <cmath>, các phiên bản quá tải của các chức năng này, với cùng một ngữ nghĩa. C++ thêm floatlong double các phiên bản quá tải của các chức năng này, với cùng ngữ nghĩa.

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

Vì vậy, bạn chỉ nên chắc chắn để bao gồm một cách chính xác <cstdlib> (int, long, long long quá tải)/<cmath> (double, float, long double quá tải).

+6

Mục 26.8.7-9 trong trường hợp có ai quan tâm. – WhozCraig

+1

tại sao quá tải bị tách ra? tại sao không đặt tất cả chúng trong một tập tin tiêu đề? –

+0

Chúng theo vị trí của các hàm C tương ứng của chúng ('abs' và' fabs'), mà tôi không chắc đó là một ý tưởng hay, vì nhiều hơn một lần nó xảy ra với tôi không phải nhập '' và do đó sử dụng số nguyên - abs trên giá trị FP. –

1

Kiểm tra xem bạn đang ở trong thực tế sử dụng std::abs từ <cstdlib> và không std::abs từ <cmath>.

PS. Ồ, chỉ cần xem chương trình mẫu, tốt thôi, có bạn đi, bạn đang sử dụng một trong những điểm quá tải nổi của std::abs .

+0

Trong thực tế, mã mẫu của anh ấy _only_ sử dụng 'cmath'. –

4

Bạn không thể đảm bảo rằng std::abs(x) sẽ luôn trả lại |x| cho tất cả các loại số học. Ví dụ, hầu hết các số thực thi có chữ ký có chỗ cho một số âm hơn số dương, do đó kết quả của abs(numeric_limits<int>::min()) sẽ không bằng |x|.

+0

Có vẻ như trường hợp duy nhất, an toàn cho 'NaN'. – Kos

+1

Và đó là trường hợp OP hits, khi anh ta chuyển đổi đôi -100000000000 thành các loại khác, cắt bớt giá trị thành giới hạn tối thiểu trong nhiều trường hợp. – nos

0

Nó không phải lạ mà g ++ (với tiêu chuẩn C++ 11) trả về một đôi khi bạn sử dụng std::abs từ <cmath> với một loại không thể thiếu: Từ http://www.cplusplus.com/reference/cmath/abs/:

Kể từ C++ 11, quá tải bổ sung là được cung cấp trong tiêu đề này (<cmath>) đối với các loại tích phân: Các tình trạng quá tải này có hiệu quả x gấp đôi trước khi tính toán (được định nghĩa cho T là bất kỳ loại tích phân nào).

này được thực sự thực hiện như thế trong /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); } 
Các vấn đề liên quan