Bitwise negation

Learn about bitwise negation in detail.

Bitwise operations handle each bit of a number individually. We use them often when programming. Let’s consider how they work in detail.

Bitwise negation

First, we will consider the simplest bitwise operation: negation. It is also called bitwise NOT. The tilde symbol (~) indicates this operation in Bash.

When doing bitwise negation, we swap the value of each bit of an integer. This means that we replace each 1 to 0, and vice versa.

Here is an example of doing bitwise NOT for the number 5:

5 = 101
~5 = 010

The bitwise NOT is a simple operation when we talk about mathematics. However, there are pitfalls when we use it in programming. We should keep two things in mind:

  • How many bytes does the number occupy?
  • What is the number’s representation in memory?

Let’s suppose that the two-byte variable stores the number 5. Then, it looks like this in memory:

00000000 00000101

When we apply the bitwise NOT for this variable, we get the following result:

11111111 11111010

What do these bits mean? If we store them to the variable of the unsigned integer type, we get the number 65530. If the variable is a signed integer, it equals -6. We can check it by converting the two’s complement representation to decimal.

Various Bash built-ins and operators represent integers in different ways. For example, echo always outputs numbers as signed integers. The printf command allows us to choose between a signed and unsigned integer.

There are no types in the Bash language. Bash stores all scalar variables as strings. It converts strings to integers right before inserting them into arithmetic expressions. The number interpretation, signed or unsigned, depends on the context.

Maximum and minimum allowed integers in Bash

Bash allocates 64 bits of memory space for each integer, regardless of its sign. The table below shows maximum and minimum allowed integers in Bash.

Integer Hexadecimal Decimal
Maximum positive signed 7FFFFFFFFFFFFFFF 9223372036854775807
Minimum negative signed 8000000000000000 -9223372036854775808
Maximum unsigned FFFFFFFFFFFFFFFF 18446744073709551615

The following examples show us how Bash interprets integers in the (( operator, echo and printf built-ins:

$ echo $((16#FFFFFFFFFFFFFFFF))
-1

$ printf "%llu\n" $((16#FFFFFFFFFFFFFFFF))
18446744073709551615

$ if ((18446744073709551615 == 16#FFFFFFFFFFFFFFFF)); then echo "ok"; fi
ok

$ if ((-1 == 16#FFFFFFFFFFFFFFFF)); then echo "ok"; fi
ok

$ if ((18446744073709551615 == -1)); then echo "ok"; fi
ok

Run the commands discussed in this lesson in the terminal below.

Get hands-on with 1200+ tech skills courses.