I want to find absolute of (a-b), where a, b are 32-Bit unsigned integer. I have used std::labs as shown below. But the operation is behaving differently in different platforms!
#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;
//Problematic operation
result_labs = labs(x - y); //undefined behavior accross different platforms
//To Replicate the behavior I tried these methods and
//able to reproduce the results in same platform
result_abs = abs(x - y);
result_llabs = static_cast<uint32_t>(llabs(x - y));
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:
operations on unsigned integer using std::labs is producing different results in different platform's. e.g gcc linux , ghs platforms How to correctly handle this abs difference computation?
[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
CodePudding user response:
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 int
, not uint32_t
. Use PRIu32
from inttypes.h
, or use C :
#include <cinttypes>
int main() {
uint32_t val;
printf("%"PRIu32"\n", val);
// or, for example explicit C-style cast:
printf("%u\n", (unsigned)val);
}
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.