Prashanth Ellina
Prashanth Ellina

Reputation: 374

Issue with absolute value of 64 bit integer

This C code tries to find the absolute value of a negative number but the output also is negative. Can anyone tell me how to overcome this?

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>

int main() {
    int64_t a = 0x8000000000000000;
    a = llabs(a);
    printf("%" PRId64 "\n", a);
    return 0;
}

Output

-9223372036854775808

UPDATE:

Thanks for all your answers. I understand that this is a non-standard value and that is why I am unable to perform an absolute operation on it. However, I did encounter this in an actual codebase that is a Genetic Programming simulation. The "organisms" in this do not know about the C standard and insist on generating this value :) Can anyone tell me an efficient way of working around this? Thanks again.

Upvotes: 3

Views: 6259

Answers (6)

Nathaniel Tagg
Nathaniel Tagg

Reputation: 415

Interestingly:

int64_t value = std::numeric_limits<int64_t>::max();
std::out << abs(value) << std::endl;

yields a value of 1 on gcc-9.

Frustrating!

Upvotes: 0

roadrunner84
roadrunner84

Reputation: 11

No signed integer with its highest bit set high and all other bits low is representable in the same type as absolute value of that integer.

Observe an 8-bit integer

int8_t x = 0x80; // binary 1000_0000, decimal -128

An 8-bit signed integer can hold values between -128 and +127 inclusive, so the value +128 is out of range. For a 16-bit integer this hols as well

int16_t = 0x8000; // binary 1000_0000_0000_0000, decimal -32,768

A 16-bit integer can hold values between -32,768 and +32,767 inclusive.

This pattern holds for any size integer as long as it is represented in two's complement, as is the de-facto representation for integers in computers. Two's complement holds 0 as all bits low and -1 as all bits high.

So an N-bit signed integer can hold values between 2^(N-1) and 2^(N-1)-1 inclusive, an unsigned integer can hold values between 0 and 2^N-1 inclusive.

Upvotes: 1

caf
caf

Reputation: 239071

If the result of llabs() cannot be represented in the type long long, then the behaviour is undefined. We can infer that this is what's happening here - the out-of-range value 0x8000000000000000 is being converted to the value -9223372036854775808 when converted to int64_t, and your long long value is 64 bits wide, so the value 9223372036854775808 is unrepresentable.

In order for your program to have defined behaviour, you must ensure that the value passed to llabs() is not less than -LLONG_MAX. How you do this is up to you - either modify the "organisms" so that they cannot generate this value (eg. filter out those that create the out-of-range value as immediately unfit) or clamp the value before you pass it to llabs().

Upvotes: 7

Yu Hao
Yu Hao

Reputation: 122403

A signed 64-bit integer ranges from −(2^63) to 2^63 − 1, The absolute value of 0x8000000000000000, or −(2^63), is 2^63, is bigger than the max 64-bit integer.

Upvotes: 1

Keith Thompson
Keith Thompson

Reputation: 263337

Basically, you can't.

The range of representable values for int64_t is -263 to +263-1. (And the standard requires int64_t to have a pure 2's-complement representation; if that's not supported, an implementation just won't define int64_t.)

That extra negative value has no corresponding representable positive value.

So unless your system has an integer type bigger than 64 bits, you're just not going to be able to represent the absolute value of 0x8000000000000000 as an integer.

In fact, your program's behavior is undefined according to the ISO C standard. Quoting section 7.22.6.1 of the N1570 draft of the 2011 ISO C standard:

The abs, labs, and llabs functions compute the absolute value of an integer j. If the result cannot be represented, the behavior is undefined.

For that matter, the result of

int64_t a = 0x8000000000000000;

is implementation-defined. Assuming long long is 64 bits, that constant is of type unsigned long long. It's implicitly converted to int64_t. It's very likely, but not guaranteed, that the stored value will be -263, or -9223372036854775808. (It's even permitted for the conversion to raise an implementation-defined signal, but that's not likely.)

(It's also theoretically possible for your program's behavior to be merely implementation-defined rather than undefined. If long long is wider than 64 bits, then the evaluation of llabs(a) is not undefined, but the conversion of the result back to int64_t is implementation-defined. In practice, I've never seen a C compiler with long long wider than 64 bits.)

If you really need to represent integer values that large, you might consider a multi-precision arithmetic package such as GNU GMP.

Upvotes: 5

Matt Bryant
Matt Bryant

Reputation: 4961

0x8000000000000000 is the smallest number that can be represented by a signed 64-bit integer. Because of quirks in two's complement, this is the only 64-bit integer with an absolute value that cannot be represented as a 64-bit signed integer.

This is because 0x8000000000000000 = -2^63, while the maximum representable 64-bit integer is 0x7FFFFFFFFFFFFFFF = 2^63-1.

Because of this, taking the absolute value of this is undefined behaviour that will generally result in the same value.

Upvotes: 2

Related Questions