← All Posts
DSA · Bit Manipulation· Part 1 of 32

Number System and Base Conversion

Bit manipulation begins before the first &, |, or ^. You need to be comfortable reading the same value in different number systems: decimal for humans, binary for bits, octal for 3-bit groups, and hexadecimal for compact 4-bit groups.

A number system is defined by its base, also called its radix. Base conversion is not magic: the value stays the same, but the groups used to write it change.

Positional Notation

In base b, each position has a place value. From right to left, the place values are 1, b, b2, b3, .... A digit tells how many of that place value are used. Digits stop at b - 1 because reaching b creates one full group, which is written as 1 in the next position and 0 in the current position.

dkdk-1...d1d0 in base b means:

d[k] * b^k + d[k-1] * b^(k-1) + ... + d[1] * b + d[0]
347 base 10 = 3 * 10^2 + 4 * 10^1 + 7 * 10^0
1011 base 2 = 1 * 2^3  + 0 * 2^2  + 1 * 2^1 + 1 * 2^0 = 11
2F base 16  = 2 * 16^1 + 15 * 16^0 = 47

Common Bases in Programming

BaseNameDigitsC++ literal styleWhy it matters
2Binary0, 10b101010Actual bit patterns used by bitwise operators.
8Octal0..7052Each octal digit equals exactly 3 binary bits.
10Decimal0..942Default notation humans usually use.
16Hexadecimal0..9, A..F0x2AEach hex digit equals exactly 4 binary bits, so it is ideal for masks.

Converting Decimal to Any Base

To convert decimal N to base b, repeatedly divide by b. Record each remainder. The first remainder is the rightmost digit, so read the remainders from bottom to top.

Why it works: every number can be written as N = b * quotient + remainder. The remainder is the rightmost digit because everything to its left is a multiple of b. Dividing by b removes that rightmost digit and exposes the next one.

Convert 45 decimal to binary:

45 / 2 = 22 remainder 1
22 / 2 = 11 remainder 0
11 / 2 = 5  remainder 1
 5 / 2 = 2  remainder 1
 2 / 2 = 1  remainder 0
 1 / 2 = 0  remainder 1

Read bottom-to-top: 101101
45 = 101101 base 2
Convert 687 decimal to hexadecimal:

687 / 16 = 42 remainder 15  -> F
 42 / 16 = 2  remainder 10  -> A
  2 / 16 = 0  remainder 2   -> 2

Read bottom-to-top: 2AF
687 = 2AF base 16

Converting Any Base to Decimal

To convert from base b to decimal, expand every digit by its positional weight and sum the terms.

Why it works: digits in base b already mean digit * place value. Decimal conversion simply evaluates that same value using decimal arithmetic.

Convert 101101 base 2 to decimal:

1 * 2^5 + 0 * 2^4 + 1 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0
= 32 + 0 + 8 + 4 + 0 + 1
= 45

Faster Mental Method: Left to Right

Instead of writing powers explicitly, process digits from left to right. Repeatedly multiply the current answer by the base and add the next digit.

2AF base 16:

ans = 0
ans = ans * 16 + 2  = 2
ans = ans * 16 + 10 = 42
ans = ans * 16 + 15 = 687

Why it works: adding a new digit on the right shifts the old value one position left, which means multiply by the base, then add the new digit.

Converting Negative Numbers

For ordinary bases such as binary, octal, decimal, hexadecimal, or base 7, the base itself is positive. A negative value is handled by converting the absolute value and then attaching a leading minus sign. The sign is not one of the base digits.

Simple logic: the digits describe the size; the minus sign says "take the negative of that size." So -1011012 means -(1011012).

Negative Decimal to Desired Base

To convert a negative decimal number -N to base b:

  1. Ignore the sign and convert N to base b using repeated division.
  2. Put - in front of the converted digits.
Convert -45 decimal to binary:

Convert +45 first:
45 / 2 = 22 remainder 1
22 / 2 = 11 remainder 0
11 / 2 = 5  remainder 1
 5 / 2 = 2  remainder 1
 2 / 2 = 1  remainder 0
 1 / 2 = 0  remainder 1

+45 = 101101 base 2
-45 = -101101 base 2

Negative Desired Base to Decimal

To convert a negative number written in base b back to decimal, remove the leading minus sign, convert the remaining digits normally, and then negate the decimal answer.

Convert -101101 base 2 to decimal:

Ignore the sign first:
101101 base 2 = 1 * 2^5 + 0 * 2^4 + 1 * 2^3 + 1 * 2^2 + 0 * 2 + 1
              = 32 + 8 + 4 + 1
              = 45

Apply the sign:
-101101 base 2 = -45 decimal
Representation note: -1011012 is mathematical signed notation in a positive base, not an exotic negative-radix system and not a fixed-width two's-complement bit pattern. For example, -45 in an 8-bit signed integer is stored as 11010011, not as the characters -101101. The next section shows the conversion, and the next post goes deeper into storage details.

Signed Binary and Two's Complement

If someone says "signed binary" in programming, they usually mean a fixed-width two's-complement representation. You must know the width first, such as 8-bit, 16-bit, 32-bit, or 64-bit. The same decimal value has different signed binary strings at different widths.

Simple logic: with w bits, the machine has exactly 2w bit patterns. Read as unsigned, they go from 0 to 2w - 1. Fixed-width arithmetic behaves like a circle: after 2w - 1, the next value wraps back to 0.

For a negative value n = -x, two's complement stores the pattern that is x steps before zero on that circle. That pattern is 2w - x. Since n is -x, the same formula is written as 2w + n.

Range of a signed w-bit integer: -2w - 1 to 2w - 1 - 1

This range comes from splitting the circle of 2w patterns into two equal halves. Patterns whose leftmost bit is 0 are the lower half, so they stay non-negative: 0 to 2w - 1 - 1. Patterns whose leftmost bit is 1 are the upper half, so two's complement reads them by subtracting one full circle: 2w.

Upper-half pattern as unsignedTwo's-complement signed valueMeaning
2w - 12w - 1 - 2w = -2w - 1First negative value.
2w - 1(2w - 1) - 2w = -1Last pattern before wrapping to zero.

For example, with 8 bits, the lower half 0 to 127 stays positive. The upper half 128 to 255 becomes negative by subtracting 256: 128 - 256 = -128 and 255 - 256 = -1. That is why the 8-bit signed range is -128 to 127.

▶ Visual: Two's-Complement Circle

Step through an 8-bit circle to see why unsigned 128..255 becomes signed -128..-1.

Negative Decimal to Signed Binary

The most practical hand method is to write the positive magnitude in w bits, then take its two's complement. The 2w + n formula gives the same result, but the bit operation is usually easier to understand first.

To convert a negative decimal number -x to w-bit signed binary:

  1. Check that -x is inside the signed w-bit range.
  2. Convert x, the positive magnitude, to binary using exactly w bits.
  3. Invert every bit.
  4. Add 1, keeping only w bits.
Convert -45 decimal to 8-bit signed binary:

8-bit signed range = -128 to 127, so -45 fits.

Step 1: write +45 in 8 bits
45 decimal = 00101101

Step 2: invert every bit
00101101 -> 11010010

Step 3: add 1
11010010
+       1
---------
11010011

Therefore:
-45 as 8-bit signed binary = 11010011

Why this works: inverting the bits gives the one's complement. Adding 1 gives the two's complement, the pattern that cancels the positive magnitude under fixed-width wraparound. In 8-bit arithmetic, 00101101 is 45 and 11010011 is the pattern that makes 45 + 11010011 wrap to 0.

Important shortcut: the same conversion can be computed as 2w - x, or 2w + n when the original value is n = -x. For -45 in 8 bits: 28 + (-45) = 256 - 45 = 211, and 211 is 11010011.

This matters because a negative signed value is stored as a large unsigned pattern in the upper half of the w-bit circle. Encoding uses 2w + n; decoding that upper-half pattern reverses the idea with U - 2w.

Signed Binary to Decimal

To convert a w-bit signed binary string to decimal:

  1. If the leftmost bit is 0, convert it as an ordinary positive binary number.
  2. If the leftmost bit is 1, convert it as an unsigned value U, then compute U - 2w.
Convert 11010011 as 8-bit signed binary:

Leftmost bit is 1, so the value is negative.

Unsigned value:
11010011 base 2 = 211 decimal

Signed value:
211 - 2^8 = 211 - 256 = -45

Therefore:
11010011 as 8-bit signed binary = -45

Why subtract 2w? A leading 1 means the unsigned value is in the negative half of the circle, so subtract one full circle to get the signed value.

Width matters: 11010011 as an 8-bit signed number is -45, but as a 16-bit signed number written as 00000000 11010011, it is 211. A signed interpretation always needs the bit width.

Fast Conversion Between Binary, Octal, and Hex

Binary, octal, and hexadecimal are special because their bases are powers of two: 8 = 23 and 16 = 24. That means conversion can be done by grouping bits instead of doing full arithmetic.

Simple logic: one hex digit matches exactly 4 bits because 16 = 24. One octal digit matches exactly 3 bits because 8 = 23.

Binary to Hexadecimal

Group binary bits into chunks of four from right to left. Pad the leftmost group with zeros if needed. Convert each 4-bit group to one hex digit.

Convert 110101101 base 2 to hexadecimal:

Pad left: 0001 1010 1101
Groups:   0001 1010 1101
Hex:         1    A    D

110101101 base 2 = 1AD base 16

Hexadecimal to Binary

Replace each hex digit by its 4-bit binary group.

Convert 0x2F to binary:

2 -> 0010
F -> 1111

0x2F = 0010 1111 base 2

Binary to Octal

Octal uses 3-bit groups because 8 = 23.

Convert 101101 base 2 to octal:

Group by 3 bits: 101 101
Octal digits:      5   5

101101 base 2 = 55 base 8

Converting From One Base to Another

The most reliable general strategy is a two-step bridge through decimal:

Simple logic: representation changes, value does not. Decimal is just a familiar checkpoint before regrouping the same value into the target base.

  1. Convert the source number from base a to decimal.
  2. Convert that decimal value to the target base b.
Convert 735 base 8 to hexadecimal:

Step 1: octal -> decimal
735 base 8 = 7 * 8^2 + 3 * 8 + 5 = 477

Step 2: decimal -> hexadecimal
477 / 16 = 29 remainder 13 -> D
 29 / 16 = 1  remainder 13 -> D
  1 / 16 = 0  remainder 1  -> 1

735 base 8 = 1DD base 16

When both bases are powers of two, skip decimal and regroup through binary bits instead.

Fractional Parts

Fractional conversion has two common directions: decimal fraction to another base, and source-base fraction back to decimal.

First, understand what a decimal with a fractional part means. The same place-value idea continues after the decimal point, but now each step divides by 10 instead of multiplying by 10.

365.42
= 3 * 10^2 + 6 * 10^1 + 5 * 10^0 + 4 * 10^-1 + 2 * 10^-2
= 300 + 60 + 5 + 0.4 + 0.02

So the left side has ones, tens, hundreds, and so on. The right side has tenths, hundredths, thousandths, and so on. In base b, the same idea becomes b-1, b-2, b-3, ....

After the point, place values keep dividing by the base:

d[k]...d[1]d[0].d[-1]d[-2]...d[-m] base b means:

d[k] * b^k + ... + d[1] * b + d[0]
+ d[-1] * b^-1 + d[-2] * b^-2 + ... + d[-m] * b^-m

Decimal Fraction to Base

To convert a decimal fraction to base b, multiply the fraction by b. The integer part of the result becomes the next digit. Keep the remaining fraction and repeat.

Why it works: suppose the answer is 0.d1d2d3... in base b. Its value is d1 * b-1 + d2 * b-2 + d3 * b-3 + .... Multiplying by b gives d1 + d2 * b-1 + d3 * b-2 + ..., so d1 becomes the integer part. Remove it, repeat, and the next digit becomes visible.

Convert 0.625 decimal to binary:

0.625 * 2 = 1.25   -> digit 1, remaining 0.25
0.25  * 2 = 0.5    -> digit 0, remaining 0.5
0.5   * 2 = 1.0    -> digit 1, remaining 0

0.625 decimal = 0.101 base 2

In this example, 0.625 * 2 = 1.25 shifts the 1 / 2 place in front of the point, so the first bit is 1. The remaining 0.25 is the still-hidden part after that shift. Multiplying it by 2 shifts again; the integer part is 0, so the second bit is 0. One more shift gives integer part 1, so the third bit is 1.

Base Fraction to Decimal

To convert a fractional number from base b to decimal, expand every digit using positional weights. Digits left of the point use non-negative powers like b0, b1, b2. Digits right of the point use negative powers: b-1, b-2, b-3, and so on.

Why it works: digits after the point use the negative-power weights from the formula above. The first digit has weight b-1 = 1 / b, the second has weight b-2 = 1 / b2, and so on.

Convert 101.101 base 2 to decimal:

Integer side:
1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 4 + 0 + 1 = 5

Fraction side:
1 * 2^-1 + 0 * 2^-2 + 1 * 2^-3
= 1/2 + 0/4 + 1/8
= 0.5 + 0 + 0.125
= 0.625

Final answer:
101.101 base 2 = 5.625 decimal

Faster Fraction Method: Right to Left

The fractional side has a useful mirror trick. For digits after the point, process from right to left. Repeatedly add the current digit, then divide by the base.

Convert 0.101 base 2 to decimal:

Process fractional digits from right to left: 1, 0, 1

ans = 0
ans = (ans + 1) / 2 = 0.5
ans = (ans + 0) / 2 = 0.25
ans = (ans + 1) / 2 = 0.625

0.101 base 2 = 0.625 decimal

Why it works: the rightmost fractional digit has the smallest place value. Dividing by the base moves the partial answer one place farther right. So 0.1012 becomes (1 + (0 + (1 / 2)) / 2) / 2, which equals 0.625.

Fraction Horner form: for 0.d1d2...dk in base b, scan from dk to d1 using ans = (ans + digit) / b.

When converting decimal fractions to another base, some values never terminate. For example, 0.1 repeats forever in base 2, which is why floating-point arithmetic can produce results like 0.30000000000000004.

Common Mistakes

Summary

With number systems in place, the next post explains how integers are actually stored in binary, including unsigned ranges, signed numbers, two's complement, overflow, and sign extension.

Practice Problems

These problems reinforce base conversion, binary strings, and hexadecimal representation.