Card 06/ 08
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
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)x: 2147483649
x >> 31: 1
without the mask: True
with the mask: Truepublic 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));
}
}x: -2147483647
x >> 31: -1
without the mask: false
with the mask: true
x >>> 31: 1const 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);x: -2147483647
x >> 31: -1
without the mask: false
with the mask: true
x >>> 31: 1The runs agree on the masked test and disagree on the unmasked one:
| Language | x | x >> 31 | Without the mask | With the mask |
|---|---|---|---|---|
| Python | 2147483649 | 1 | True | True |
| Java | −2147483647 | −1 | false | true |
| TypeScript | −2147483647 | −1 | false | true |
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.
← → move through sections and cards