44

I saw the following line of code here in C.

 int mask = ~0;

I have printed the value of mask in C and C++. It always prints -1.

So I do have some questions:

  • Why assigning value ~0 to the mask variable?
  • What is the purpose of ~0?
  • Can we use -1 instead of ~0?
Jayesh
  • 4,755
  • 9
  • 32
  • 62
  • 39
    `~0` is only equal to `-1` in 2's complement – phuclv Sep 23 '17 at 06:21
  • Can you show how mask is used? Otherwise we can only guess. – Paul Floyd Sep 23 '17 at 06:21
  • 1
    @PaulFloyd: the linked source is about a pure bit-fiddling exercise... as useful as lifting weights – 6502 Sep 23 '17 at 06:33
  • 7
    Related: [Is it safe to use -1 to set all bits to true?](https://stackoverflow.com/questions/809227/is-it-safe-to-use-1-to-set-all-bits-to-true) – Cody Gray - on strike Sep 23 '17 at 10:41
  • 19
    Using signed type for a mask tells me there are terrible things happeing in your code. – Sopel Sep 23 '17 at 12:58
  • 1
    Possible duplicate of [What does ~ operator do?](https://stackoverflow.com/questions/3952122/what-does-operator-do) – Bernhard Barker Sep 23 '17 at 19:04
  • The answers to "why" and "what is the purpose" would both be "to do whatever the programmer wanted to do" - it would be like asking "what is the purpose of `int x=y+z`", its purpose would depend entirely on whatever is being done with the result. – Bernhard Barker Sep 23 '17 at 19:11

6 Answers6

81

It's a portable way to set all the binary bits in an integer to 1 bits without having to know how many bits are in the integer on the current architecture.

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • 7
    `-1` also sets all the bits in an integer to 1 without knowing the width of `int` type. It just implies the use of two's complement – phuclv Sep 23 '17 at 06:24
  • 26
    @LưuVĩnhPhúc correct. The ~0 method has fewer dependencies. Works on unsigned, non-two's compliment systems and is (arguably) less cryptic. – Richard Hodges Sep 23 '17 at 07:02
  • `long n = ~0;` works too, but `unsigned long u = ~0u;` might not. – chqrlie Sep 23 '17 at 07:36
  • 3
    @chqrlie why not? BTW using signed values for the bitmasks is a weird idea. – 0___________ Sep 23 '17 at 08:01
  • 1
    @PeterJ_01: If `long` has more bits than `int`, then `~0u` (which has type `unsigned`) would be _zero-extended_ as part of initializing `u`. – hmakholm left over Monica Sep 23 '17 at 12:45
  • chucks sorry I have not noticed that u. Anyway 0u in this assignment is a weird idea if we try to load max possible unsigned value for the type. – 0___________ Sep 23 '17 at 13:34
  • @PeterJ_01: I purposely wrote `~0u` because it seems logical to use an unsigned constant for an unsigned expression... But the devil is in the detail. Of course `unsigned long u = ~0UL;` works fine. – chqrlie Sep 23 '17 at 15:52
  • Zero in this case is specific and after bitwise negation it will be treated as unsigned. There is no need of the u suffix. Any sufix destroys the logic of this assignment (to be integer size independent) – 0___________ Sep 23 '17 at 16:09
  • @Lưu Vĩnh Phúc: It also implies that you a) really know what twos complement is (it's been a long time since that undergrad CS class :-)); and b) are absolutely sure that your code will always run on a twos complement. – jamesqf Sep 23 '17 at 16:12
  • @jamesqf I don't think it implies you are absolutely sure that your code will always run on a twos complement machine; I would also consider "assumed all machines use twos complement" another possible disjunct in the implication (as I could easily imagine making that mistake myself). – Daniel Wagner Sep 23 '17 at 17:42
  • well, IMHO, if you write a `unsigned long u = ~0u` instead of `unsigned long u = ~0ul`, than it's a typo and your fault not some quirk or UB. That's what unit test would catch. – quetzalcoatl Sep 23 '17 at 18:21
  • 5
    This is not portable if on one's complement: it gives a negative zero, which a conforming compiler may decide is a trap representation. At that point you have UB. To be fair, such an implementation may be said to not *have* an all-ones value for `int` in the first place. – Kevin Sep 23 '17 at 20:46
  • A question linked in the comments states that `~0` is not as portable as implied here – D. Ben Knoble Sep 23 '17 at 23:19
  • @Kevin: can trap representations be obtained as the result of arithmetic or bitwise operations on integers? – chqrlie Sep 24 '17 at 07:05
  • @chqrlie: Certainly, try `INT_MAX + 1`. – Kevin Sep 24 '17 at 07:10
  • @Kevin: of course signed overflow is a good example. Arithmetic overflow does not seem to apply to bitwise operations and what happens to padding bits is not specified in the Standard. But you have a good poiont: on a one's complement architecture where negative zero is a trap representation (such as a carefully configured DS9K) , `~0` would have undefined behavior. – chqrlie Sep 24 '17 at 07:24
36

C and C++ allow 3 different signed integer formats: sign-magnitude, one's complement and two's complement

~0 will produce all-one bits regardless of the sign format the system uses. So it's more portable than -1

You can add the U suffix (i.e. -1U) to generate an all-one bit pattern portably1. However ~0 indicates the intention clearer: invert all the bits in the value 0 whereas -1 will show that a value of minus one is needed, not its binary representation

1 because unsigned operations are always reduced modulo the number that is one greater than the largest value that can be represented by the resulting type

phuclv
  • 37,963
  • 15
  • 156
  • 475
8

That on a 2's complement platform (that is assumed) gives you -1, but writing -1 directly is forbidden by the rules (only integers 0..255, unary !, ~ and binary &, ^, |, +, << and >> are allowed).

6502
  • 112,025
  • 15
  • 165
  • 265
5

You are studying a coding challenge with a number of restrictions on operators and language constructions to perform given tasks.

The first problem is return the value -1 without the use of the - operator.

On machines that represent negative numbers with two's complement, the value -1 is represented with all bits set to 1, so ~0 evaluates to -1:

/* 
 * minusOne - return a value of -1 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 2
 *   Rating: 1
 */
int minusOne(void) {
  // ~0 = 111...111 = -1
  return ~0;
}

Other problems in the file are not always implemented correctly. The second problem, returning a boolean value representing the fact the an int value would fit in a 16 bit signed short has a flaw:

/* 
 * fitsShort - return 1 if x can be represented as a 
 *   16-bit, two's complement integer.
 *   Examples: fitsShort(33000) = 0, fitsShort(-32768) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 1
 */
int fitsShort(int x) {
  /* 
   * after left shift 16 and right shift 16, the left 16 of x is 00000..00 or 111...1111
   * so after shift, if x remains the same, then it means that x can be represent as 16-bit
  */
  return !(((x << 16) >> 16) ^ x); 
}

Left shifting a negative value or a number whose shifted value is beyond the range of int has undefined behavior, right shifting a negative value is implementation defined, so the above solution is incorrect (although it is probably the expected solution).

chqrlie
  • 131,814
  • 10
  • 121
  • 189
2

Loooong ago this was how you saved memory on extremely limited equipment such as the 1K ZX 80 or ZX 81 computer. In BASIC, you would

Let X = NOT PI

rather than

LET X = 0

Since numbers were stored as 4 byte floating points, the latter takes 2 bytes more than the first NOT PI alternative, where each of NOT and PI takes up a single byte.

skaak
  • 2,988
  • 1
  • 8
  • 16
  • Not exactly. In those examples all (key)words were encoded by single bytes, as well a the equals sign, variable names and the _visible_ zero itself. However, the numeric constants were followed by (an escape byte?) plus 4-byte floating-point value, which was utilized by the executor but not shown in the source. Hence the size diference here is 3 or even 4 bytes, not 2. – CiaPan Sep 24 '17 at 15:12
  • I should be working ... just did this on some ZX Spectrum emulator and saved it to tape / disk. The first one uses 40 and the seond 35 bytes, so a 5 byte difference. Well, if you have just 1k as on the ZX 81, I guess that is half a percent saving in file size and which is why it was used ... Back to the OP question - would you be able to save a byte or two (or 5) today by using say x = ~0 rather than x = -1? Maybe using a conversion such as long x = ~0 ? – skaak Oct 04 '17 at 12:24
  • I really should be working ... just attempted this in c in a few simple ways but the compiled file was the same size for -1, ~0 and ~(0x00) ... I am sure there are some architectures where this will save a byte or so but such oddities must of course be avoided at all costs nowadays. The compiled files were roughly 8.5k bytes. This would give the poor ZX81 a heart attack ... – skaak Oct 04 '17 at 12:41
  • 1
    Modern C compilers are smart enough to recognize `~0` and `-1` as the same value. They can also optimize the code either to maximum speed or to minimum size, so you would have to play with compiling options. Furthermore, the compiled code can be rounded up to 4, 8 or 16 bytes for better aligning of functions' entry points, so looking at `obj` file size is unsuitable for your pusrpose. You might rather compile the C code to assembly, or even disassemble the compiled object module (with some tool like objdump) to see the byte-by-byte representation of your code. – CiaPan Oct 09 '17 at 07:32
  • is there no integer type on those systems? And how can `PI` be stored in a single byte? – phuclv Jun 30 '18 at 12:36
  • This specific system had a single number type, which combined floats and ints if I remember correctly. Any number, e.g. 1 or 0 or 3.141 or whatever used 4 bytes. On the other hand, all keywords (like if, then, else, pi, sin, cos, tan, goto etc) was converted into a single byte. So to save memory programmers would do x = not pi in stead of x = 0 as explained. Crazy unreadable stuff, but so was code in those days and on those systems ... – skaak Jul 02 '18 at 07:56
0

There are multiple ways of encoding numbers across all computer architectures. When using 2's complement this will always be true:~0 == -1. On the other hand, some computers use 1's complement for encoding negative numbers for which the above example is untrue, because ~0 == -0. Yup, 1s complement has negative zero, and that is why it is not very intuitive.

So to your questions

  • the ~0 is assigned to mask so all the bits in mask are equal 1 -> making mask & sth == sth
  • the ~0 is used to make all bits equal to 1 regardless of the platform used
  • you can use -1 instead of ~0 if you are sure that your computer platform uses 2's complement number encoding

My personal thought - make your code as much platform-independent as you can. The cost is relatively small and the code becomes fail proof

bartop
  • 9,971
  • 1
  • 23
  • 54
  • Only if the 1s complement implementation supports negative zeros. See also [this answer](https://stackoverflow.com/a/46387882/6717178) – JHBonarius Sep 24 '17 at 08:05