Converting Between Uppercase and Lowercase Using Bitwise Operations
TL;DR Leveraging the 32-unit difference between uppercase and lowercase letters in ASCII, a single ch ^= 32 bitwise operation quickly toggles letter case.
Bitwise operations are among the most fundamental and efficient operations in computer science. Today, let’s explore a classic application: converting between uppercase and lowercase letters with a single line of code.
Why Use Bitwise Operations for Case Conversion?
Your first reaction might be: doesn’t Java already have Character.toUpperCase() and Character.toLowerCase()? Why reinvent the wheel?
Indeed, 99% of the time you should use the standard library. But understanding this trick has several benefits:
- Common interview topic: Many algorithm interviews test this kind of bitwise thinking
- Understanding low-level computing: Appreciate the elegance of ASCII code design
- Embedded/low-level development: In environments without standard libraries, this is how it’s done
- Code brevity:
ch ^= 32is far more elegant thanCharacter.isUpperCase(ch) ? Character.toLowerCase(ch) : ch
The Elegant Design of ASCII
To understand this trick, first examine how English letters are encoded in the ASCII table:
| Letter | Decimal | Hexadecimal | Binary |
|---|---|---|---|
| A | 65 | 0x41 | 0100 0001 |
| a | 97 | 0x61 | 0110 0001 |
| B | 66 | 0x42 | 0100 0010 |
| b | 98 | 0x62 | 0110 0010 |
| Z | 90 | 0x5A | 0101 1010 |
| z | 122 | 0x7A | 0111 1010 |
Notice the pattern: the ASCII values of uppercase and lowercase letters differ by exactly 32 (0x20). At the binary level, this is the difference in the 6th bit (counting from the right, starting at 0):
- Uppercase letters have the 6th bit as 0 (e.g., A: 0100 0001)
- Lowercase letters have the 6th bit as 1 (e.g., a: 0110 0001)
This is the beauty of ASCII’s design — case conversion requires flipping just a single bit!
Three Bitwise Approaches
1. XOR (Toggle): Switch Between Cases
1 | ch ^= 32; // equivalent to ch ^= 0x20 |
The ^ (XOR) rule is “same gives 0, different gives 1”. XOR-ing with 32 (where the 6th bit is 1) flips only that bit: uppercase becomes lowercase, lowercase becomes uppercase.
2. Force to Uppercase
1 | ch &= ~32; // equivalent to ch &= 0xDF |
~32 (bitwise NOT) makes the 6th bit 0 while leaving all other bits as 1. The & operation clears the 6th bit, so regardless of input case, the output is always uppercase.
3. Force to Lowercase
1 | ch |= 32; // equivalent to ch |= 0x20 |
The | operation sets the 6th bit to 1. Regardless of input case, the output is always lowercase.
Edge Cases
The above operations only work for English letters. They are meaningless for non-letter characters, so validate first:
1 | if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') { |




