13
$ echo $(( 255 ))
255
$ echo $(( 33 ))
33
$ echo $(( ~33 ))
-34
$ echo $(( ~255 ))
-256
$ 

and my kernel is:

$ uname -a
Linux HOSTNAME 3.2.0-40-generic-pae #64-Ubuntu SMP Mon Mar 25 21:44:41 UTC 2013 i686 i686 i386 GNU/Linux

QUESTION: ~ is for negating the number AFAIK. But why does ~33 produce -34 and why does ~255 produce -256?

  • 3
    Bitwise negation, not to be confused with arithmetic negation (-x) – chepner May 10 '13 at 19:31
22

The man page of bash says:

   ! ~    logical and bitwise negation

Signed numbers are usually stored in Two's complement representation:

...
-4 = 1100
-3 = 1101
-2 = 1110
-1 = 1111
 0 = 0000
 1 = 0001
 2 = 0010
 3 = 0011
...

This means if you take a number like 2 it is bitwise interpreted as 0010. After bitwise negation this becomes 1101, which is the representation of -3.

| improve this answer | |
11

This is the result of two's complement arithmetic.

~ is a bitwise negation that inverts all of the bits being operated upon. Two's complement arithmetic works by inverting all of the bits and adding 1. Since you have only flipped the bits, but not added one, you get the same number, inverted, minus one.

Wikipedia has a good article on two's complement here.

As an example:

  • 3 in binary is 0011
  • -3 in (two's complement) binary is 1101
  • Inverting 0011 gives you 1100, which is -4, since you haven't added 1.
| improve this answer | |
4

The ~ operator is the bitwise NOT operator. Using it is not the same as negating a number.

From wikipedia, a bitwise NOT operation is equal to taking the two's complement of the value minus one:

NOT x = −x − 1

Negating a binary number is equivalent to taking its two-complement value.

Using the ~ NOT operator = take its one-complement value.

In simpler terms, ~ just flips all the bits of the binary representation.

For your examples:

33 (decimal) = 0x00100001 (8-bit binary)

~33 = ~0x00100001 = 0x11011110 = -34 (decimal)

Or in decimal arithmetics, using the ~x = -x - 1 formula:

~33 = -33 - 1 = -34

and

~255 = -255 - 1 = -256

| improve this answer | |
2

The ~ (arithmetic) operator flips all bits, it is called the bitwise negation operator:

! ~    logical and bitwise negation

So, in places where the context is arithmetic, it changes a number with all bits as zeros to all bits as ones. A $(( ~0 )) converts all the bits of the number representation (usually 64 bits nowadays) to all ones.

$ printf '%x\n' "$(( ~0 ))"
ffffffffffffffff

A number with all ones is interpreted as the negative number (first bit 1) 1, or simply -1.

$ printf '%x\n' "-1"
ffffffffffffffff

$ echo "$(( ~0 ))"
-1

The same happens to all other numbers, for example: $(( ~1 )) flips all bits:

$ printf '%x\n' "$(( ~1 ))"
fffffffffffffffe

Or, in binary: 1111111111111111111111111111111111111111111111111111111111111110

Which, interpreted as a number in two's representation is:

$ echo "$(( ~1 ))"
-2

In general, the human math equation is that $(( ~n )) is equal to $(( -n-1 ))

$ n=0    ; echo "$(( ~n )) $(( -n-1 ))"
-1 -1

$ n=1    ; echo "$(( ~n )) $(( -n-1 ))"
-2 -2

$ n=255  ; echo "$(( ~n )) $(( -n-1 ))"
-256 -256

And (your question):

$ n=33   ; echo "$(( ~n )) $(( -n-1 ))"
-34 -34
| improve this answer | |
1

The problem is that ~ is a bit-wise operator. Hence you are negating more bits than you perhaps intend. You can see this better by converting the results to hex e.g.:

result_in_hex=$(printf "%x" $(( ~33 ))); echo $result_in_hex
ffffffffffffffde

versus what you had:

result_in_dec=$(printf "%d" $(( ~33 ))); echo $result_in_dec
-34

I'm assuming you mean to negate 0x33. If that is the case then this would work:

result_in_hex=$(printf "%2x" $(( ( ~ 0x33 ) & 0xFF))); echo $result_in_hex
cc

You need to also use & which is the bit-wise and operator to avoid all the ff at the start.

| improve this answer | |
0

First you have to understand that 33 is a 32 bits or a 64 bits number.

For conveniance I take an eight bit number (= 1 byte)

decimal 33 is in eight bits: 00100001, flipping the bits results in 11011110.

Because the high order bit is 1, it is a negative number.

Printing a negative number, the system prints a minus sign and then does a two's complement on the negative number.

Two's complement is: flipping the bits and adding 1.

11011110 ==> 00100001 ==> adding 1 ==> 00100010 results in decimal 34 behind the minus sign.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.