Does abs(unsigned long) make any sense?

后端 未结 3 1193
梦毁少年i
梦毁少年i 2021-02-04 05:37

I\'ve come across this code, which incidentally my profiler reports as a bottleneck:

#include 

unsigned long a, b;
// Calculate values for a and         


        
3条回答
  •  礼貌的吻别
    2021-02-04 06:08

    I don't know whether you'd regard it as making sense, but abs() applied to an unsigned value can certainly return a value other than the one passed in. That's because abs() takes an int argument and returns an int value.

    For example:

    #include 
    #include 
    
    int main(void)
    {
        unsigned u1 = 0x98765432;
        printf("u1 = 0x%.8X; abs(u1) = 0x%.8X\n", u1, abs(u1));
        unsigned long u2 = 0x9876543201234567UL;
        printf("u2 = 0x%.16lX; abs(u2) = 0x%.16lX\n", u2, labs(u2));
        return 0;
    }
    

    When compiled as C or C++ (using GCC 4.9.1 on Mac OS X 10.10.1 Yosemite), it produces:

    u1 = 0x98765432; abs(u1) = 0x6789ABCE
    u2 = 0x9876543201234567; abs(u2) = 0x6789ABCDFEDCBA99
    

    If the high bit of the unsigned value is set, then the result of abs() is not the value that was passed to the function.

    The subtraction is merely a distraction; if the result has the most significant bit set, the value returned from abs() will be different from the value passed to it.


    When you compile this code with C++ headers, instead of the C headers shown in the question, then it fails to compile with ambiguous call errors:

    #include 
    #include 
    using namespace std;
    
    int main(void)
    {
        unsigned u1 = 0x98765432;
        cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(u1) << "\n";
        unsigned long u2 = 0x9876543201234567UL;
        cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n";
        return 0;
    }
    

    Compilation errors:

    absuns2.cpp: In function ‘int main()’:
    absuns2.cpp:8:72: error: call of overloaded ‘abs(unsigned int&)’ is ambiguous
         cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(u1) << "\n";
                                                                            ^
    absuns2.cpp:8:72: note: candidates are:
    In file included from /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:72:0,
                     from absuns2.cpp:1:
    /usr/include/stdlib.h:129:6: note: int abs(int)
     int  abs(int) __pure2;
          ^
    In file included from absuns2.cpp:1:0:
    /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:174:3: note: long long int std::abs(long long int)
       abs(long long __x) { return __builtin_llabs (__x); }
       ^
    /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:166:3: note: long int std::abs(long int)
       abs(long __i) { return __builtin_labs(__i); }
       ^
    absuns2.cpp:10:72: error: call of overloaded ‘abs(long unsigned int&)’ is ambiguous
         cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(u2) << "\n";
                                                                            ^
    absuns2.cpp:10:72: note: candidates are:
    In file included from /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:72:0,
                     from absuns2.cpp:1:
    /usr/include/stdlib.h:129:6: note: int abs(int)
     int  abs(int) __pure2;
          ^
    In file included from absuns2.cpp:1:0:
    /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:174:3: note: long long int std::abs(long long int)
       abs(long long __x) { return __builtin_llabs (__x); }
       ^
    /usr/gcc/v4.9.1/include/c++/4.9.1/cstdlib:166:3: note: long int std::abs(long int)
       abs(long __i) { return __builtin_labs(__i); }
       ^
    

    So, the code in the question only compiles when only the C-style headers are used; it doesn't compile when the C++ headers are used. If you add as well as , there's an additional overload available to make the calls more ambiguous.

    You can make the code compile if you add (in)appropriate casts to the calls to abs(), and the absolute value of a signed quantity can be different from the original signed quantity, which is hardly surprising news:

    #include 
    #include 
    using namespace std;
    
    int main(void)
    {
        unsigned u1 = 0x98765432;
        cout << "u1 = 0x" << hex << u1 << "; abs(u1) = 0x" << hex << abs(static_cast(u1)) << "\n";
        unsigned long u2 = 0x9876543201234567UL;
        cout << "u2 = 0x" << hex << u2 << "; abs(u2) = 0x" << hex << abs(static_cast(u2)) << "\n";
        return 0;
    }
    

    Output:

    u1 = 0x98765432; abs(u1) = 0x6789abce
    u2 = 0x9876543201234567; abs(u2) = 0x6789abcdfedcba99
    

    Moral: Don't use the C headers for which there are C++ equivalents in C++ code; use the C++ headers instead.

提交回复
热议问题