Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a difference between -1 and ~0?

Tags:

c++

c

When comparing a unsigned value, as in this test:

if (pos == (size_t)-1)

Is this comparison technically different from something like:

if (pos == (size_t)~0)

I am not used to the second variant. That's why I am asking the question. The answer may be rather straighforward if it's yes.

like image 596
yves Baumes Avatar asked Jul 09 '14 09:07

yves Baumes


3 Answers

The C++ standard guarantees that size_t is an unsigned type, that unsigned types obey the usual modular arithmetic rules (where the modulus is two to the number of bits in the value representation of the type, cf. 3.9/4), and thus -1 converted to size_t must be the largest value which that type can represent.

The value 0 is an int, and ~0 has all the bits in the int representation of zero flipped. The value of that result depends on the representation of int on your platform. That value (which may be a trap representation, thanks @Matt McNabb) is then converted to size_t (which is done following the rules of modular arithmetic).

In conclusion, whether the resulting values compare equal is implementation defined. (For example, if int is represented in two's complement, then the value of ~0 is -1, so the two are the same.)

like image 138
Kerrek SB Avatar answered Oct 08 '22 10:10

Kerrek SB


Assuming (guaranteed by the standard) that size_t refers to an unsigned integer value, this:

if(pos == (size_t)~0)

used with the intent to be equivalent to:

if(pos == (size_t)-1)

is assuming that the machine uses a 2's complement representation for negative integers. The standard doesn't enforce it so you shouldn't assume it if you want your code to be 100% portable.

like image 19
Marco A. Avatar answered Oct 08 '22 10:10

Marco A.


So, in your example technically there is no difference whatsoever. Because it is hard to find a compiler that will not optimize operations on literals like -1 and ~0. With your example I've got exactly:

        ; ...
        movq    $-1, -16(%rbp)
        movq    $-1, -8(%rbp)
        ; ...

Don't be afraid of those -1's, assebmly is typeless ;)

More interesting question is if your example would be:

#include <stddef.h>
int main() {
        int var0 = 0;
        int var1 = 1;
        size_t a = (size_t) -var1;
        size_t b = (size_t) ~var0;
        return a ^ b;
}

In my case (Kubuntu, gcc 4.8.2, x86_64, -O0 option) the part of interest was:

        movl    $0, -24(%rbp)    ; var0 = 0
        movl    $1, -20(%rbp)    ; var1 = 1

        movl    -20(%rbp), %eax
        negl    %eax             ; 2's complement negation

        ; ...

        movl    -24(%rbp), %eax
        notl    %eax             ; 1's complement negation

        ; ...

Looking into Intel's manual:

NEG - Two's Complement Negation

Replaces the value of operand (the destination operand) with its two's complement. (This operation is equivalent to subtracting the operand from 0.)

NOT - One's Complement Negation

Performs a bitwise NOT operation (each 1 is set to 0, and each 0 is set to 1) on the destination operand and stores the result in the destination operand location.

My conclusion would be, theoretically, code could differ on some exotic platform and compiler, but otherwise – no. And always if unsure check assembly listing on your platform.

like image 1
hurufu Avatar answered Oct 08 '22 10:10

hurufu