We usually write numbers with ten digits: `0` through `9`. A computer, however, is built from electronic components that are very good at distinguishing between two states: on and off, high voltage and low voltage, or simply `1` and `0`.
That is where binary comes from: a number system that uses only two digits.
Binary is not just a strange way to write numbers. Understanding it explains how computers store information, manage permissions, turn flags on and off, perform fast operations, handle negative numbers, and sometimes produce surprising results because of overflow.
Base 10 versus base 2
In decimal notation, the value of each digit depends on its position and a power of 10.
583 = 5*100 + 8*10 + 3*1
= 5*10^2 + 8*10^1 + 3*10^0Binary uses the same idea, but each position is based on a power of 2:
... 32 16 8 4 2 1For example:
1011_2Place the bits under the column values:
value: 8 4 2 1
bit: 1 0 1 1So:
1011_2 = 8 + 0 + 2 + 1 = 11_10The rightmost digit always represents `2^0`, which is 1. Moving left, the positions are `2^1`, `2^2`, `2^3`, and so on.
Converting decimal to binary
One convenient method is to break the number into powers of 2.
Convert 45:
45 = 32 + 8 + 4 + 1Write the columns:
32 16 8 4 2 1Mark `1` where the value is used, and `0` where it is not:
32 16 8 4 2 1
1 0 1 1 0 1Therefore:
45_10 = 101101_2Another method is to divide by 2 repeatedly and record the remainders. Then read the remainders from bottom to top.
Converting binary to decimal
Take:
1010110_2Break it into columns:
64 32 16 8 4 2 1
1 0 1 0 1 1 0So:
64 + 16 + 4 + 2 = 86Which means:
1010110_2 = 86_10Binary addition and subtraction
Binary addition is similar to decimal addition, but there are only four basic cases:
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 10The result `10` means: write `0` in the current column and carry `1` into the next column.
1011
+ 0101
------
10000For subtraction, when you need to compute `0-1`, you borrow from the column on the left. The borrowed bit becomes `10_2` in the current column, meaning two units.
10101
- 00110
-------
01111Decimal check:
21 - 6 = 15
01111_2 = 15During borrowing, the temporary values are column-by-column decompositions. They are not necessarily a normal binary number you should read as one continuous sequence.
Bit shifts in Java
Java has operators that shift all bits left or right.
Left shift: `<<`
int result = 13 << 1;The binary representation of 13 is:
001101After one left shift:
011010So:
13 << 1 = 26In general, shifting left by `n` positions is usually equivalent to multiplying by `2^n`:
x << 1 ~= x * 2
x << 2 ~= x * 4
x << 3 ~= x * 8The `~=` matters. The equality holds only when no bits are lost and no overflow changes the result.
Right shift: `>>`
For a positive number, shifting right is usually equivalent to integer division by a power of 2:
int result = 29 >> 1; // 14In binary:
11101 -> 01110The bit that was discarded on the right represented the remainder. In this case it was `1`, so:
29 / 2 = 14 remainder 1Logical operations on bits
Bitwise operations compare two numbers column by column.
Use these two values:
A = 10110
B = 01101AND: `&`
The result is `1` only when both bits are `1`.
10110
& 01101
-------
00100OR: `|`
The result is `1` when at least one bit is `1`.
10110
| 01101
-------
11111XOR: `^`
The result is `1` when the bits are different.
10110
^ 01101
-------
11011The key difference between OR and XOR appears when both bits are `1`:
1 OR 1 = 1
1 XOR 1 = 0NOT: `~`
NOT flips every bit:
0 -> 1
1 -> 0In a theoretical five-bit width:
~10110 = 01001But a Java `int` has 32 bits, so `~` flips all 32 bits, including the ones we did not show in the short example.
Practical use: storing permissions in one number
Suppose a system has four permissions:
private static final int READ = 1; // 0001
private static final int WRITE = 1 << 1; // 0010
private static final int EXECUTE = 1 << 2; // 0100
private static final int ADMIN = 1 << 3; // 1000The number:
0101represents two active permissions:
READ = 1
WRITE = 0
EXECUTE = 1
ADMIN = 0So the user has read and execute permissions.
Enabling a permission with OR
permissions |= WRITE;0101 | 0010 = 0111Checking a permission with AND
boolean canExecute = (permissions & EXECUTE) != 0;If the AND result is not zero, the bit exists.
Disabling a permission with AND and NOT
permissions &= ~READ;`~READ` creates a mask where the READ bit is `0` and all other bits are `1`. AND then turns off only READ.
Toggling a permission with XOR
permissions ^= EXECUTE;If EXECUTE was enabled, it becomes disabled. If it was disabled, it becomes enabled.
Checking one permission, any permission, or all permissions
Suppose the mask contains two permissions:
int mask = READ | WRITE;To check whether at least one permission exists:
boolean hasAny = (permissions & mask) != 0;To check whether all permissions in the mask exist:
boolean hasAll = (permissions & mask) == mask;These are different questions.
permissions = 0101
mask = 0011AND result:
0101 & 0011 = 0001So:
hasAny = true
hasAll = falseThere is one shared permission, READ, but WRITE is missing.
Fixed width and overflow
In mathematics, you can add more digits when you need them. In a computer, each type has a fixed number of bits.
Suppose only four bits can be stored:
1101_2 = 13A left shift produces the full result:
1101 << 1 = 11010_2 = 26But `11010` needs five bits. If only four bits are allowed, the leftmost bit is discarded:
11010
↓↓↓↓
1010The stored result is:
1010_2 = 10The discarded bit represented 16:
26 - 16 = 10That is overflow: the operation happened, but there was not enough room to represent the full result.
In Java, an `int` has 32 bits. When a result exceeds that range, the extra bits are discarded and the result can even change sign without throwing an error.
Negative numbers: the difference between `>>` and `>>>`
Java provides two right-shift operators.
Arithmetic shift: `>>`
The `>>` operator copies the sign bit from the left.
For a negative number, the sign bit is `1`, so the number remains negative:
-8 >> 1 == -4Logical shift: `>>>`
The `>>>` operator always inserts `0` on the left, even when the number is negative.
-8 >>> 1 == 2147483644The result is large and positive because the leftmost bit became `0`, while the remaining bits stayed active.
-16 >> 2 == -4
-16 >>> 2 == 1073741820Why `/` and `>>` differ for negative odd numbers
Consider:
-11 / 2
-11 >> 1The mathematical result is:
-11 / 2 = -5.5Integer division with `/` in Java truncates toward zero:
-11 / 2 == -5An arithmetic right shift behaves like rounding down in this case:
-11 >> 1 == -6So you should not automatically replace division by 2 with a right shift when the value may be negative.
Also remember: `//` is not a division operator in Java. It starts a comment until the end of the line.
int result = -11 / 2; // the result is -5A complete Java experiment
public class BinaryExperiment {
private static final int READ = 1;
private static final int WRITE = 1 << 1;
private static final int EXECUTE = 1 << 2;
private static final int ADMIN = 1 << 3;
public static void main(String[] args) {
int number = 45;
System.out.println(
"45 in binary: " + Integer.toBinaryString(number)
);
int parsed = Integer.parseInt("101101", 2);
System.out.println("101101 in decimal: " + parsed);
System.out.println("13 << 2: " + (13 << 2));
System.out.println("29 >> 1: " + (29 >> 1));
int permissions = READ | EXECUTE;
printPermissions("Initial", permissions);
permissions |= WRITE;
printPermissions("Enable WRITE", permissions);
permissions ^= EXECUTE;
printPermissions("Toggle EXECUTE", permissions);
permissions &= ~READ;
printPermissions("Disable READ", permissions);
permissions |= ADMIN;
printPermissions("Enable ADMIN", permissions);
int mask = WRITE | ADMIN;
boolean hasAny = (permissions & mask) != 0;
boolean hasAll = (permissions & mask) == mask;
System.out.println("Has any required permission: " + hasAny);
System.out.println("Has all required permissions: " + hasAll);
System.out.println("-11 / 2: " + (-11 / 2));
System.out.println("-11 >> 1: " + (-11 >> 1));
System.out.println("-16 >>> 2: " + (-16 >>> 2));
}
private static void printPermissions(String title, int value) {
String binary = String.format(
"%4s",
Integer.toBinaryString(value)
).replace(' ', '0');
System.out.println(title + ": " + binary + " (" + value + ")");
}
}Common mistakes
The first mistake is reading a binary number as if it were decimal. `1011_2` is not one thousand and eleven. It is 11.
Another mistake is assuming that shifting left always means multiplication and shifting right always means division. That is only safe when no bits are lost, no overflow occurs, and negative numbers are handled correctly.
With bit masks, this check:
(permissions & mask) != 0asks whether at least one shared bit exists. It does not check whether all bits in the mask exist. To check all of them, use:
(permissions & mask) == maskIt is also important to distinguish between the result of AND, which is a number, and the comparison to `0`, which produces a `boolean`.
Finally, Java's `~` operator does not flip only the bits displayed on the screen. It flips every bit of the type.
Summary
Binary is the same positional system we know from base 10, only with powers of 2 and the digits `0` and `1`.
Understanding binary makes it easier to read and build bit masks, manage permissions and flags, understand shifts, recognize overflow, and avoid wrong assumptions about negative numbers.
The goal is not to do every binary calculation in your head. The goal is to understand what the bits represent, how an operation changes them, and what can happen when the number has a fixed width.
Once you see a number as a set of individual bits, operators that used to feel mysterious, such as `&`, `|`, `^`, `~`, `<<`, `>>`, and `>>>`, become clear and useful tools.