GotchaDifficulty: Intermediate2 min

Testing the Sign Bit With a Shift Alone

(x >> 31) == 1 is true in Python and false in Java and TypeScript, because there a shift copies the sign bit into every place it vacates.

Testing whether bit 31 is on with (x >> 31) == 1 works in Python and fails in Java and TypeScript, because there >> copies the sign bit into every position it vacates. That kind of shift is an arithmetic shift: it keeps a negative number negative, so a 32-bit value with bit 31 on shifts to −1, every bit 1, never to 1. A Python integer has no sign bit at position 31, so the same expression gives 1.

The value (1 << 31) | 1 has bits 31 and 0 on. Each listing tests bit 31 without the mask and with & 1, which keeps only the lowest bit of the shifted value.

The same sign test in three languages

sign_bit.pypy
x = (1 << 31) | 1
print("x:", x)
print("x >> 31:", x >> 31)
print("without the mask:", (x >> 31) == 1)
print("with the mask:", ((x >> 31) & 1) == 1)
OutputRan with python:3.14.8-slim-trixie (Python 3.14.8)
x: 2147483649
x >> 31: 1
without the mask: True
with the mask: True
SignBit.javajava
public class SignBit {
    public static void main(String[] args) {
        int x = (1 << 31) | 1;
        System.out.println("x: " + x);
        System.out.println("x >> 31: " + (x >> 31));
        System.out.println("without the mask: " + ((x >> 31) == 1));
        System.out.println("with the mask: " + (((x >> 31) & 1) == 1));
        System.out.println("x >>> 31: " + (x >>> 31));
    }
}
OutputRan with eclipse-temurin:25.0.4.1_1-jdk-noble (Java 25.0.4.1)
x: -2147483647
x >> 31: -1
without the mask: false
with the mask: true
x >>> 31: 1
sign_bit.tsts
const x = (1 << 31) | 1;
console.log("x:", x);
console.log("x >> 31:", x >> 31);
console.log("without the mask:", (x >> 31) === 1);
console.log("with the mask:", ((x >> 31) & 1) === 1);
console.log("x >>> 31:", x >>> 31);
OutputRan with node:24.22.0-trixie-slim (Node 24.22.0), TypeScript 5.9.3
x: -2147483647
x >> 31: -1
without the mask: false
with the mask: true
x >>> 31: 1

The runs agree on the masked test and disagree on the unmasked one:

The same bits tested in three languages, from each run
Languagexx >> 31Without the maskWith the mask
Python21474836491TrueTrue
Java−2147483647−1falsetrue
TypeScript−2147483647−1falsetrue

Java and TypeScript also have the unsigned shift >>>, which fills with 0, so x >>> 31 is 1 there. Either fix, & 1 after the shift or >>> in place of >>, tests the bit itself rather than the sign. The mistake survives testing on small numbers, where bit 31 is 0 and every language agrees; it shows only on values with the top bit on, such as hash codes and the results of other bit operations.