简体   繁体   中英

undefined behavior with std::labs across multiple platforms

I want to find absolute difference of a 32-Bit integer. I have used std::labs as shown below.

#include <iostream>
#include <cstdlib>
using namespace std;

template< class T >
static inline T abs(const T& val) {
    return (val >= static_cast<T>(0)) ? val : static_cast<T>(-1)*(val);
}

int main()
{
    uint32_t x = 0, y = 0, result_labs = 0, result_abs = 0, result_llabs = 0;
    int32_t z = 0;
    for (size_t i = 0; i < 100; i++)
    {
        x = rand();
        y = rand();

        z = x - y;
        result_labs = labs(x - y);      //Correct output
        result_abs = abs(x - y);        //Undifined behaviour
        result_llabs = static_cast<uint32_t>(llabs(x - y));//Undifined behaviour

        if ((result_abs != result_labs) || (result_abs != result_llabs))
        {
            printf("[Error] X: %d       Y: %d      z: %d   \tlabs: %d    -!= abs: %d    labs: %d\n", x, y, z, result_labs, result_abs, result_llabs);
        }
    }

    return 0;
}

Problem:

std::labs is producing wrong results in different platform. So I tried to implement my own abs function as follows. But that is also producing wrong results. What is the correct way to implement the std::labs in c++? or is there any substitute for the same?

How to correctly handle this behavior in

[Error] X: 41       Y: 18467      z: -18426     labs: 18426    -!= abs: -18426    labs: -18426
[Error] X: 6334       Y: 26500      z: -20166           labs: 20166    -!= abs: -20166    labs: -20166
[Error] X: 11478       Y: 29358      z: -17880          labs: 17880    -!= abs: -17880    labs: -17880
[Error] X: 5705       Y: 28145      z: -22440           labs: 22440    -!= abs: -22440    labs: -22440
[Error] X: 2995       Y: 11942      z: -8947    labs: 8947    -!= abs: -8947    labs: -8947
[Error] X: 4827       Y: 5436      z: -609      labs: 609    -!= abs: -609    labs: -609

The behavior is defined (except may be for the printf ).

You call the functions with x - y argument. Both x and y are uint32_t so the result is also uint32_t , so it will never be negative . Arithmetic operations on unsigned types "wraps around".

labs takes long argument, so the argument is converted to long before passing to the function. So uint32_t is converted to long , which is implementation-defined, but basically means that values greater then LONG_MAX result in a negative value.

Your abs is a template called with uint32_t type, because the argument has uint32_t type. uint32_t will never be negative, so (val >= static_cast<T>(0)) is just always true, and it is an identity function.

llabs takes long long argument, so the argument is converted to long long . long long has at least 64-bits, LLONG_MAX is at least somewhere around 2^63-1 . Any value of type uint32_t is representable with long long . uint32_t is never negative, converting to long long does not change the value, so llabs just receives a positive value, so llabs it just does nothing and returns the original value.

Your printf calls may be invalid - %u is for printing unsigned , not uint32_t . Use PRIu32 from inttypes.h , or use C++.

What is the correct way to implement the std::labs in c++?

long labs(long x) {
   return x < 0 ? -x : x;
}

is just enough. Note that the types are explicitly long.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM