39

According to wiki shifts can be used to calculate powers of 2:

A left arithmetic shift by n is equivalent to multiplying by 2^n (provided the value does not overflow), while a right arithmetic shift by n of a two's complement value is equivalent to dividing by 2^n and rounding toward negative infinity.

I was always wondering if any other bitwise operators (~,|,&,^) make any mathematical sense when applied to base-10? I understand how they work, but do results of such operations can be used to calculate anything useful in decimal world?

Daniel Egeberg
  • 8,359
  • 31
  • 44
serg
  • 109,619
  • 77
  • 317
  • 330
  • 1
    Yes. You can calculate the remainder of a power of 2 (2^n) by using the bitwise and with (2^n - 1). – Heath Hunnicutt Jul 23 '10 at 16:00
  • 1
    I took this question to mean "do bitwise-operators have any meaningful representation in base-10," but it seems everyone else thinks it's about floating-point numbers. – BlueRaja - Danny Pflughoeft Jul 23 '10 at 17:21
  • 2
    @BlueRaja yep base-10 is what I mean, maybe the question is worded bad? – serg Jul 23 '10 at 17:32
  • @serg Not really answering your question but whenever I need to quickly calculate binary operator stuff using decimal, I use this site: http://www.bitwiseoperatorcalculator.com – jonprasetyo Sep 20 '14 at 18:32

6 Answers6

29
"yep base-10 is what I mean"

In that case, yes, they can be extended to base-10 in several ways, though they aren't nearly as useful as in binary.

One idea is that &, |, etc. are the same as doing arithmetic mod-2 to the individual binary digits. If a and b are single binary-digits, then

a & b = a * b (mod 2)
a ^ b = a + b (mod 2)
   ~a = 1-a   (mod 2)
a | b = ~(~a & ~b) = 1 - (1-a)*(1-b) (mod 2)

The equivalents in base-10 would be (note again these are applied per-digit, not to the whole number)

a & b = a * b (mod 10)
a ^ b = a + b (mod 10)
   ~a = 9-a   (mod 10)
a | b = ~(~a & ~b) = 9 - (9-a)*(9-b) (mod 10)

The first three are useful when designing circuits which use BCD (~a being the 9's complement), such as non-graphing calculators, though we just use * and + rather than & and ^ when writing the equations. The first is also apparently used in some old ciphers.

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • 4
    Alternatively you can also view & as the minimum, | as the maximum and ~ again as the complement. Mathematically this gives you a lattice instead of a ring. – starblue Jul 24 '10 at 06:57
14

A fun trick to swap two integers without a temporary variable is by using bitwise XOR:

void swap(int &a, int &b) {
   a = a ^ b;
   b = b ^ a; //b now = a
   a = a ^ b; //knocks out the original a
}

This works because XOR is a commutative so a ^ b ^ b = a.

Naseer Mohammad
  • 389
  • 4
  • 14
Rich
  • 12,068
  • 9
  • 62
  • 94
  • 1
    Although sadly, most typed languages won't let you do XOR on non-integers. It's a shame... otherwise, it'd let you swap anything. – Sam Dufel Aug 26 '11 at 03:16
  • 3
    @SamDufel: Actually it would only swap the bitwise representations of anything. Any structure containing internal pointers (i.e. one part of the data structure pointing to another one, as for example in the short string optimization of string types) would break because now the pointer would point into the *other* object. – celtschk Dec 29 '17 at 09:31
  • @celtschk - I'm not sure what point you're trying to make. I wasn't attempting to write an exhaustive treatise on pointer manipulation - this was just an offhand comment. – Sam Dufel Jan 10 '18 at 21:29
  • 2
    @SamDufel: The point I was making is that your claim that “it'd let you swap anything” is wrong. – celtschk Jan 10 '18 at 23:34
  • 6
    I believe you're mistaken, but I don't think a pedantic argument about what constitutes "swapping" will add value to this answer. – Sam Dufel Jan 10 '18 at 23:41
  • 1
    @Rich This answers only `do results of such operations can be used to calculate anything useful in decimal world?` part of question and should clearly mention so. – Gaurav Singh Jun 24 '19 at 06:43
  • @garavsingh, I think being able to swap two integers without an intermediate variable *is* a useful application in the decimal world, no? – Rich Jul 10 '19 at 20:47
7

Yes, there are other useful operations, but they tend to be oriented towards operations involving powers of 2 (for obvious reasons), e.g. test for odd/even, test for power of 2, round up/down to nearest power of 2, etc.

See Hacker's Delight by Henry S. Warren.

Paul R
  • 208,748
  • 37
  • 389
  • 560
6

In every language I've used (admittedly, almost exclusively C and C-derivatives), the bitwise operators are exclusively integer operations (unless, of course, you override the operation).

While you can twiddle the bits of a decimal number (they have their own bits, after all), it's not necessarily going to get you the same result as twiddling the bits of an integer number. See Single Precision and Double Precision for descriptions of the bits in decimal numbers. See Fast Inverse Square Root for an example of advantageous usage of bit twiddling decimal numbers.

EDIT

For integral numbers, bitwise operations always make sense. The bitwise operations are designed for the integral numbers.

n << 1 == n * 2
n << 2 == n * 4
n << 3 == n * 8

n >> 1 == n / 2
n >> 2 == n / 4
n >> 3 == n / 8

n & 1 == {0, 1}       // Set containing 0 and 1
n & 2 == {0, 2}       // Set containing 0 and 2
n & 3 == {0, 1, 2, 3} // Set containing 0, 1, 2, and 3

n | 1 == {1, n, n+1}
n | 2 == {2, n, n+2}
n | 3 == {3, n, n+1, n+2, n+3}

And so on.

Brian S
  • 4,878
  • 4
  • 27
  • 46
  • I think you're confusing the term *decimal* with *floating point* - they are not the same thing - integers can be decimal for example. – Paul R Jul 23 '10 at 16:07
  • You raise an interesting point. There are actually much better reasons for tearing apart the bits of a `float` than of an `int`. – Heath Hunnicutt Jul 23 '10 at 16:08
  • @Paul R: I assumed serg was talking about non-integral numbers, because while you may enter an integer in decimal, hexadecimal, octal, or binary in many languages, the binary representation is identical, and they're still integers. (And if you output the value without any special consideration, most languages will use decimal regardless of what you wrote for input). `0b11001000 = 0310 = 200 = 0xC8` – Brian S Jul 23 '10 at 16:20
  • @Brian: yes, I guess the question is a little ambiguous. Normally though people would say "floating point" (or "fixed point" or whatever) if they are talking about numeric types other than integers. – Paul R Jul 23 '10 at 17:07
  • Sorry, I just meant base-10 numbers (ints, but if there are some interesting applications for floats that would be good too). – serg Jul 23 '10 at 18:23
  • 1
    Then yes, bitwise operations always make sense. Bitwise operations are designed for integral numbers, regardless of base. `n << 1 == n * 2` for an integral number `n`. – Brian S Jul 23 '10 at 18:34
  • Sorry, what does `{0, 1}` mean? – serg Jul 23 '10 at 18:52
  • @serg555: `{0, 1}` would be a set containing 0 and 1. `n & 1` will result in either 0 or 1, depending on whether `n` is odd or even. – Brian S Jul 23 '10 at 21:26
  • Ok, thanks. It would be nice if you could provide some comments for every expression explaining briefly what exactly is returned, it's hard to understand what those expressions mean. – serg Jul 23 '10 at 21:42
3

You can calculate logarithms using just bitwise operators...

Finding the exponent of n = 2**x using bitwise operations [logarithm in base 2 of n]

Community
  • 1
  • 1
eruciform
  • 7,680
  • 1
  • 35
  • 47
1

You can sometime substitute bitwise operations for boolean operations. For example, the following code:

if ((a < 0) && (b < 0)
{
  do something
{

In C this can be replaced by:

if ((a & b) < 0)
{
  do something
{

This works because one bit in an integer is used as the sign bit (1 indicates negative). The and operation (a & b) will be a meaningless number, but its sign will be the bitwise and of the signs of the numbers and hence checking the sign of the result will work.

This may or may not benefit performance. Doing two boolean tests/branches will be worse on a number of architectures and compilers. Modern x86 compilers can probably generate a single branch using a some of the newer instruction even with the normal syntax.

As always, if it does result in a performance increase... Comment the code - i.e. put the "normal" way of doing it in a comment and say it's equivalent but faster.

Likewise, ~ | and ^ can be used in a similar way it all the conditions are (x<0). For comparison conditions you can generally use subtraction:

if ((a < b) | (b < c))
{
}

becomes:

if (((a-b) | (b-c)) < 0)
{
}

because a-b will be negative only if a is less than b. There can be issues with this one if you get within a factor of 2 of max int - i.e. arithmetic overflow, so be careful.

These are valid optimizations in some cases, but otherwise quite useless. And to get really ugly, floating point numbers also have sign bits... ;-)

EXAMPLE: As an example, lets say you want to take action depending on the order of a,b,c. You can do some nested if/else constructs, or you can do this:

x = ((a < b) << 2) | ((b < c) << 1) | (c < a);
switch (x):

I have used this in code with up to 9 conditions and also using the subtractions mentioned above with extra logic to isolate the sign bits instead of less-than. It's faster than the branching equivalent. However, you no longer need to do subtraction and sign bit extraction because the standard was updated long ago to specify true as 1, and with conditional moves and such, the actual less-than can be quite efficient these days.

phkahler
  • 5,687
  • 1
  • 23
  • 31