Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 17 – Bit manipulation

Bits across languages

Test, set, clear and toggle bits with masks, then see how Python, JavaScript, Java, C++ and Go differ on negative numbers, shifts and binary text.

  • Beginner
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25.0.4.1, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free: 30 minutes

What you will learn

  • Use AND, OR, XOR, NOT and shifts to test, set, clear and toggle bits
  • Predict negative numbers and shifts in Python, JavaScript, Java, C++ and Go
  • Convert between bases and binary strings, including 32-bit patterns
  • Avoid the precedence and bit-31 mistakes that small tests rarely catch

Before you start

What you’ll learn · Before you start

What you will learn

  • Use AND, OR, XOR, NOT and shifts to test, set, clear and toggle bits
  • Predict negative numbers and shifts in Python, JavaScript, Java, C++ and Go
  • Convert between bases and binary strings, including 32-bit patterns
  • Avoid the precedence and bit-31 mistakes that small tests rarely catch

Before you start

On this page

An integer is a row of bits, so one 32-bit integer can hold 32 yes-or-no facts, one per bit. Four expressions do most of the work, and they look the same in Python, JavaScript, Java, C++ and Go. With the mask 1 << i, which has only bit i on, x & (1 << i) tests bit i, x | (1 << i) sets it, x & ~(1 << i) clears it and x ^ (1 << i) toggles it. Go writes NOT as ^x and has an extra operator, &^, for clearing; the rest is spelled the same.

The integers underneath are not the same. Python’s integers have no fixed width, JavaScript turns its numbers into 32-bit integers for every bitwise operator, Java’s int and long are always signed, and C++ and Go have unsigned types as well. So one expression can give different answers in different languages for a negative number, for bit 31, for a shift by 32 places and for binary text. This lesson runs the same small programs in all five languages and shows what each one printed, so that you know your language’s answer before a hidden test finds it for you.

Masks and the four moves

Number the bits from the right, starting at 0, so that bit i is worth 2ⁱ. In eight bits, 106 is 0110 1010, because 106 = 64 + 32 + 8 + 2: bits 6, 5, 3 and 1 are on. A mask is a number whose 1 bits mark the positions you want to work on, and 1 << i is the mask of bit i alone. Six operators work on the bits of integers:

  • a & b (AND) has a 1 wherever both a and b have a 1.
  • a | b (OR) has a 1 wherever at least one of them has a 1.
  • a ^ b (XOR) has a 1 wherever the two differ.
  • ~a (NOT, written ^a in Go) flips every bit of a.
  • a << k moves every bit k places to the left and fills the right with zeros; as long as the result still fits the type, that multiplies a by 2ᵏ.
  • a >> k moves every bit k places to the right, so the lowest k bits fall off; that divides a by 2ᵏ and rounds down.

A mask of one bit and these operators give the four moves:

  • Test: x & (1 << i) keeps bit i and turns every other bit off, so the result is 0 exactly when bit i is off.
  • Set: x | (1 << i) turns bit i on.
  • Clear: x & ~(1 << i) turns bit i off.
  • Toggle: x ^ (1 << i) flips bit i.

Set, clear and toggle leave every other bit as it was.

The byte 0110 1010 (106), then four rows: testing bit 3, setting bit 2, clearing bit 5 and toggling bit 0, each with its result.bit position76543210valuex = 10601101010106test bit 3: onx & (1 << 3)000010008set bit 2x | (1 << 2)01101110110clear bit 5x & ~(1 << 5)0100101074toggle bit 0x ^ (1 << 0)01101011107

The four moves on x = 106, each with a mask of one bit

Text description of the diagram

The diagram shows eight bit positions, numbered 7 on the left down to 0 on the right, and five rows of eight cells. The last column gives each row's value in decimal. In each move, the cell of the bit the mask touches is drawn with a thicker border.

  1. x = 106: 0110 1010, so bits 6, 5, 3 and 1 are on (64 + 32 + 8 + 2).
  2. Test bit 3 with x & (1 << 3): 0000 1000, which is 8. Every bit except bit 3 is 0 in the mask, so the result keeps only bit 3 of x; it is not 0, so bit 3 is on.
  3. Set bit 2 with x | (1 << 2): 0110 1110, which is 110. Bit 2 is now on and the other bits are unchanged.
  4. Clear bit 5 with x & ~(1 << 5): 0100 1010, which is 74. Bit 5 is now off and the other bits are unchanged.
  5. Toggle bit 0 with x ^ (1 << 0): 0110 1011, which is 107. Bit 0 flipped from 0 to 1; toggling it again would give 106 back.
The four moves on x = 106 Python · moves/four_moves.py
"""The four moves on one byte: test, set, clear and toggle a single bit of x."""


def byte(n):
    """n as 8 binary digits, in two groups of four."""
    digits = format(n, "08b")
    return digits[:4] + " " + digits[4:]


x = 106
print(f"x                 {byte(x)}  {x:>3}")
print(f"x & (1 << 3)      {byte(x & (1 << 3))}  {x & (1 << 3):>3}   test bit 3: not 0, so it is set")
print(f"x | (1 << 2)      {byte(x | (1 << 2))}  {x | (1 << 2):>3}   set bit 2")
print(f"x & ~(1 << 5)     {byte(x & ~(1 << 5))}  {x & ~(1 << 5):>3}   clear bit 5")
print(f"x ^ (1 << 0)      {byte(x ^ (1 << 0))}  {x ^ (1 << 0):>3}   toggle bit 0")
print(f"(x >> 3) & 1      {'':9}  {(x >> 3) & 1:>3}   test bit 3 as 0 or 1")

Output

x                 0110 1010  106
x & (1 << 3)      0000 1000    8   test bit 3: not 0, so it is set
x | (1 << 2)      0110 1110  110   set bit 2
x & ~(1 << 5)     0100 1010   74   clear bit 5
x ^ (1 << 0)      0110 1011  107   toggle bit 0
(x >> 3) & 1                   1   test bit 3 as 0 or 1

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 four_moves.py

The test does not give 0 or 1. It gives 0 or 2ⁱ, the value of the bit it kept: 8 for bit 3. So compare it with zero, (x & (1 << i)) != 0, or move the bit down to position 0 first: (x >> i) & 1 is exactly 0 or 1. The clear works because ~(1 << i) has every bit on except bit i, so the AND keeps everything else as it was.

One feature-flag mask in five languages

A notes app keeps each user’s settings as the bits of one 32-bit mask: dark mode is bit 0, email alerts bit 1, SMS alerts bit 2, two-factor sign-in bit 3, and early access to beta features bit 31. The program below sets three flags, clears one, toggles another, then lists the flags that are on, counts them and finds the highest. You can run the Python and JavaScript tabs on this page:

The same flag mask in five languages

Python · flags/flags.py

"""One user's settings kept in a single 32-bit mask: each bit position is one flag."""

DARK_MODE, EMAIL_ALERTS, SMS_ALERTS, TWO_FACTOR, BETA = 0, 1, 2, 3, 31
NAMES = {DARK_MODE: "dark mode", EMAIL_ALERTS: "email alerts", SMS_ALERTS: "SMS alerts", TWO_FACTOR: "two-factor", BETA: "beta"}


def show(step, flags):
    print(f"{step:<22}{flags:>12}  {flags:032b}")


flags = 0
flags |= 1 << DARK_MODE  # set a bit: OR with its mask
flags |= 1 << SMS_ALERTS
flags |= 1 << BETA
show("set dark, SMS, beta", flags)
flags &= ~(1 << SMS_ALERTS)  # clear a bit: AND with the mask inverted
show("clear SMS", flags)
flags ^= 1 << EMAIL_ALERTS  # toggle a bit: XOR with its mask
show("toggle email", flags)

on = [name for bit, name in NAMES.items() if (flags >> bit) & 1]
print(f"on: {', '.join(on)}")
print(f"flags on: {flags.bit_count()}   highest bit on: {flags.bit_length() - 1}")
print(f"beta tested with > 0: {(flags & (1 << BETA)) > 0}")

Output

set dark, SMS, beta     2147483653  10000000000000000000000000000101
clear SMS               2147483649  10000000000000000000000000000001
toggle email            2147483651  10000000000000000000000000000011
on: dark mode, email alerts, beta
flags on: 3   highest bit on: 31
beta tested with > 0: True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 flags.py

JavaScript · flags/flags.mjs

// One user's settings kept in a single 32-bit mask: each bit position is one flag.
const DARK_MODE = 0, EMAIL_ALERTS = 1, SMS_ALERTS = 2, TWO_FACTOR = 3, BETA = 31;
const NAMES = new Map([[DARK_MODE, "dark mode"], [EMAIL_ALERTS, "email alerts"], [SMS_ALERTS, "SMS alerts"], [TWO_FACTOR, "two-factor"], [BETA, "beta"]]);

function show(step, flags) {
  const bits = (flags >>> 0).toString(2).padStart(32, "0"); // >>> 0 reads the 32 bits as unsigned
  console.log(`${step.padEnd(22)}${String(flags).padStart(12)}  ${bits}`);
}

let flags = 0;
flags |= 1 << DARK_MODE; // set a bit: OR with its mask
flags |= 1 << SMS_ALERTS;
flags |= 1 << BETA;
show("set dark, SMS, beta", flags);
flags &= ~(1 << SMS_ALERTS); // clear a bit: AND with the mask inverted
show("clear SMS", flags);
flags ^= 1 << EMAIL_ALERTS; // toggle a bit: XOR with its mask
show("toggle email", flags);

const on = [...NAMES].filter(([bit]) => (flags >>> bit) & 1).map(([, name]) => name);
console.log(`on: ${on.join(", ")}`);
let count = 0;
for (let bit = 0; bit < 32; bit++) count += (flags >>> bit) & 1; // JavaScript has no built-in bit count
console.log(`flags on: ${count}   highest bit on: ${31 - Math.clz32(flags)}`);
console.log(`beta tested with > 0: ${(flags & (1 << BETA)) > 0}`);

Output

set dark, SMS, beta    -2147483643  10000000000000000000000000000101
clear SMS              -2147483647  10000000000000000000000000000001
toggle email           -2147483645  10000000000000000000000000000011
on: dark mode, email alerts, beta
flags on: 3   highest bit on: 31
beta tested with > 0: false

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node flags.mjs

Java · compiled/Flags.java

// One user's settings kept in a single 32-bit mask: each bit position is one flag.
import java.util.StringJoiner;

public class Flags {
    static final int DARK_MODE = 0, EMAIL_ALERTS = 1, SMS_ALERTS = 2, TWO_FACTOR = 3, BETA = 31;
    static final int[] BITS = {DARK_MODE, EMAIL_ALERTS, SMS_ALERTS, TWO_FACTOR, BETA};
    static final String[] NAMES = {"dark mode", "email alerts", "SMS alerts", "two-factor", "beta"};

    static void show(String step, int flags) {
        String bits = String.format("%32s", Integer.toBinaryString(flags)).replace(' ', '0');
        System.out.printf("%-22s%12d  %s%n", step, flags, bits);
    }

    public static void main(String[] args) {
        int flags = 0;
        flags |= 1 << DARK_MODE; // set a bit: OR with its mask
        flags |= 1 << SMS_ALERTS;
        flags |= 1 << BETA;
        show("set dark, SMS, beta", flags);
        flags &= ~(1 << SMS_ALERTS); // clear a bit: AND with the mask inverted
        show("clear SMS", flags);
        flags ^= 1 << EMAIL_ALERTS; // toggle a bit: XOR with its mask
        show("toggle email", flags);

        StringJoiner on = new StringJoiner(", ");
        for (int k = 0; k < BITS.length; k++) {
            if (((flags >>> BITS[k]) & 1) != 0) on.add(NAMES[k]);
        }
        System.out.println("on: " + on);
        System.out.println("flags on: " + Integer.bitCount(flags) + "   highest bit on: " + (31 - Integer.numberOfLeadingZeros(flags)));
        System.out.println("beta tested with > 0: " + ((flags & (1 << BETA)) > 0));
    }
}

C++ · compiled/flags.cpp

// One user's settings kept in a single 32-bit mask: each bit position is one flag.
#include <bit>
#include <cstdint>
#include <print>
#include <string>
#include <utility>
#include <vector>

constexpr int DARK_MODE = 0, EMAIL_ALERTS = 1, SMS_ALERTS = 2, TWO_FACTOR = 3, BETA = 31;
const std::vector<std::pair<int, std::string>> NAMES = {
    {DARK_MODE, "dark mode"}, {EMAIL_ALERTS, "email alerts"}, {SMS_ALERTS, "SMS alerts"}, {TWO_FACTOR, "two-factor"}, {BETA, "beta"}};

void show(const std::string& step, std::uint32_t flags) {
    std::println("{:<22}{:>12}  {:032b}", step, flags, flags);
}

int main() {
    std::uint32_t flags = 0;  // unsigned: bit 31 is just another bit
    flags |= 1u << DARK_MODE;  // set a bit: OR with its mask
    flags |= 1u << SMS_ALERTS;
    flags |= 1u << BETA;
    show("set dark, SMS, beta", flags);
    flags &= ~(1u << SMS_ALERTS);  // clear a bit: AND with the mask inverted
    show("clear SMS", flags);
    flags ^= 1u << EMAIL_ALERTS;  // toggle a bit: XOR with its mask
    show("toggle email", flags);

    std::string on;
    for (const auto& [bit, name] : NAMES) {
        if ((flags >> bit) & 1u) on += (on.empty() ? "" : ", ") + name;
    }
    std::println("on: {}", on);
    std::println("flags on: {}   highest bit on: {}", std::popcount(flags), std::bit_width(flags) - 1);
    std::println("beta tested with > 0: {}", (flags & (1u << BETA)) > 0);
}

Go · compiled/flags.go

// One user's settings kept in a single 32-bit mask: each bit position is one flag.
package main

import (
	"fmt"
	"math/bits"
	"strings"
)

const (
	DarkMode    = 0
	EmailAlerts = 1
	SMSAlerts   = 2
	TwoFactor   = 3
	Beta        = 31
)

var names = []struct {
	bit  int
	name string
}{{DarkMode, "dark mode"}, {EmailAlerts, "email alerts"}, {SMSAlerts, "SMS alerts"}, {TwoFactor, "two-factor"}, {Beta, "beta"}}

func show(step string, flags uint32) {
	fmt.Printf("%-22s%12d  %032b\n", step, flags, flags)
}

func main() {
	var flags uint32       // unsigned: bit 31 is just another bit
	flags |= 1 << DarkMode // set a bit: OR with its mask
	flags |= 1 << SMSAlerts
	flags |= 1 << Beta
	show("set dark, SMS, beta", flags)
	flags &^= 1 << SMSAlerts // clear a bit: Go's AND NOT operator
	show("clear SMS", flags)
	flags ^= 1 << EmailAlerts // toggle a bit: XOR with its mask
	show("toggle email", flags)

	var on []string
	for _, f := range names {
		if (flags>>f.bit)&1 != 0 {
			on = append(on, f.name)
		}
	}
	fmt.Println("on:", strings.Join(on, ", "))
	fmt.Printf("flags on: %d   highest bit on: %d\n", bits.OnesCount32(flags), bits.Len32(flags)-1)
	fmt.Println("beta tested with > 0:", flags&(1<<Beta) > 0)
}

The Java, C++ and Go versions are built and run as you would in a terminal. The last command shows two masks that Go refuses to compile:

Running the Java, C++ and Go versions Python · compiled/flags_session.py
from session import run, versions

# Flags.java runs as a single source file, flags.cpp is built with the sanitizers on, and go run builds flags.go.
# A line that starts with "!" must fail: mask_types.go holds two masks that Go refuses to compile.
versions("java -version", "clang++ --version", "go version")
run("""
java Flags.java
clang++ -std=c++23 -Wall -Wextra -fsanitize=address,undefined flags.cpp -o flags
./flags
go run flags.go
!go run mask_types.go
""")

Output

# openjdk version "25.0.4.1" LTS
# Apple clang version 21.0.0 (clang-2100.3.34.2)
# go version go1.27.1 darwin/arm64
$ java Flags.java
set dark, SMS, beta    -2147483643  10000000000000000000000000000101
clear SMS              -2147483647  10000000000000000000000000000001
toggle email           -2147483645  10000000000000000000000000000011
on: dark mode, email alerts, beta
flags on: 3   highest bit on: 31
beta tested with > 0: false
$ clang++ -std=c++23 -Wall -Wextra -fsanitize=address,undefined flags.cpp -o flags
$ ./flags
set dark, SMS, beta     2147483653  10000000000000000000000000000101
clear SMS               2147483649  10000000000000000000000000000001
toggle email            2147483651  10000000000000000000000000000011
on: dark mode, email alerts, beta
flags on: 3   highest bit on: 31
beta tested with > 0: true
$ go run flags.go
set dark, SMS, beta     2147483653  10000000000000000000000000000101
clear SMS               2147483649  10000000000000000000000000000001
toggle email            2147483651  10000000000000000000000000000011
on: dark mode, email alerts, beta
flags on: 3   highest bit on: 31
beta tested with > 0: true
$ go run mask_types.go
# command-line-arguments
./mask_types.go:8:11: 1 << 31 (untyped int constant 2147483648) overflows int32
./mask_types.go:11:14: ^(1 << 2) (untyped int constant -5) overflows uint32
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 flags_session.py

The last column, the 32 bits, is the same in every language, and so are the names of the flags that are on. The decimal number is not: 2147483651 in Python, C++ and Go, but −2147483645 in JavaScript and Java. Both are right. They are the same 32 bits read in two ways, as an unsigned number and as a signed one, where bit 31 counts as −2³¹ instead of +2³¹. Python’s integer simply grows to 2³¹ + 3. The C++ and Go versions use the unsigned types std::uint32_t and uint32, in which bit 31 is just another bit. JavaScript’s bitwise operators, except >>>, return a signed 32-bit result, and Java’s int is signed.

That difference breaks one common test. In JavaScript and Java, (flags & (1 << 31)) > 0 is false even though the beta flag is on, because the result is −2147483648. Compare with zero instead: != 0 (!== in JavaScript) works for every bit in every language. The same goes for a test that reads a bit back with >>: in Java and JavaScript, flags >> 31 copies the sign bit and gives −1, while flags >>> 31 gives 1.

The counts use each language’s built-ins: bit_count() (Python 3.10 and later) and bit_length() in Python, Integer.bitCount and Integer.numberOfLeadingZeros in Java, std::popcount and std::bit_width from C++’s <bit>, and bits.OnesCount32 and bits.Len32 from Go’s math/bits. JavaScript has no bit count, so its version loops over the 32 bits, and Math.clz32 counts the leading zero bits. The C++ and Go helpers accept unsigned types only: the C++ standard makes that a constraint of every counting function in <bit>, and math/bits works on Go’s unsigned types. That is one more reason to keep a mask in std::uint32_t or uint32.

Go checks constants against the type they are used as. An int32 mask cannot take 1 << 31, because 2147483648 is one more than the largest int32, and ^(1 << 2) is the untyped constant −5, which no uint32 can hold. Both are compile errors, which is better than a wrong answer: use uint32 masks, and clear bits with &^.

Negative numbers are two’s complement

In all five languages, the bitwise operators see a negative integer in two’s complement. In n bits, −x is stored as 2ⁿ − x, so in eight bits −5 is 256 − 5 = 251, which is 1111 1011. Read the other way, the top bit counts as −2ⁿ⁻¹ instead of +2ⁿ⁻¹: −128 + 64 + 32 + 16 + 8 + 2 + 1 = −5. Two facts follow in every language. −1 has every bit on, and ~x equals -x - 1, so ~5 is −6; the Java Language Specification states that rule for ~ in those words.

Python has no fixed width, so how many bits does −5 have? Its documentation defines the bitwise operators as if every negative number had infinitely many sign bits: an endless row of 1s to the left of 1011. The program shows what that means in practice:

Negative numbers in Python Python · negatives/negatives.py
"""Negative numbers in Python: two's complement with an endless row of sign bits on the left."""


def to_int32(u):
    """The low 32 bits of u, read as a signed 32-bit integer (what a Java int or a JavaScript bitwise result holds)."""
    u &= 0xFFFFFFFF
    return u - (1 << 32) if u >> 31 else u


x = -5
print("bits 7 to 0 of -5   ", "".join(str((x >> i) & 1) for i in range(7, -1, -1)))
print("bit 40 of -5        ", (x >> 40) & 1)
print("bin(-5)             ", bin(x))
print("-5 & 0xFF           ", x & 0xFF, "=", format(x & 0xFF, "08b"))
print("-5 & 0xFFFFFFFF     ", x & 0xFFFFFFFF)
print("~5 and ~-6          ", ~5, ~-6)
print("-1 >> 1, -1 >> 99   ", -1 >> 1, -1 >> 99)
for u in (4294967291, 1 << 31, (1 << 31) - 1, 1 << 32):
    print(f"to_int32({u})".ljust(20), to_int32(u))

Output

bits 7 to 0 of -5    11111011
bit 40 of -5         1
bin(-5)              -0b101
-5 & 0xFF            251 = 11111011
-5 & 0xFFFFFFFF      4294967291
~5 and ~-6           -6 5
-1 >> 1, -1 >> 99    -1 -1
to_int32(4294967291) -5
to_int32(2147483648) -2147483648
to_int32(2147483647) 2147483647
to_int32(4294967296) 0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 negatives.py

  • Every bit of −5 above bit 2 is 1, bit 40 included, and -1 >> 99 is still −1: shifting right keeps feeding in copies of the sign bit.
  • bin(-5) prints -0b101, a minus sign and the magnitude, not the bits as stored. To see the bits, choose a width and keep only those bits: -5 & 0xFF is 251, the eight-bit pattern, and -5 & 0xFFFFFFFF is 4294967291, the 32-bit one.
  • To read 32 bits back as a signed number, the way Java or JavaScript would, subtract 2³² when bit 31 is on. to_int32 does that; it is the step to take whenever a Python answer must match a 32-bit language.

The exercise at the end of this lesson puts the four moves and this conversion together.

Shifts: where the five languages part ways

Right shifts of negative numbers, shifts that fill with zeros, and shifts by the full width are where the five languages disagree. Each program below prints the same rows, written in its own language. C++ moves the shift by 32 into a program of its own, and Go adds a row for its 64-bit int:

Five shifts in five languages

Python · shifts/shifts.py

"""Shifting a negative number, and shifting 1 by 31 and 32 places, in Python."""

x = -7
rows = [
    ("-7 >> 1", x >> 1),  # right shift rounds down
    ("-7 // 2", x // 2),  # so does //, Python's integer division
    ("-7 >>> 1 (32 bits)", (x & 0xFFFFFFFF) >> 1),  # no >>> operator: mask to 32 bits, then shift
    ("1 << 31", 1 << 31),
    ("1 << 32", 1 << 32),
]
for label, value in rows:
    print(f"{label:<20}{value}")

Output

-7 >> 1             -4
-7 // 2             -4
-7 >>> 1 (32 bits)  2147483644
1 << 31             2147483648
1 << 32             4294967296

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 shifts.py

JavaScript · shifts/shifts.mjs

// Shifting a negative number, and shifting 1 by 31 and 32 places, in JavaScript.
const x = -7;
const rows = [
  ["-7 >> 1", x >> 1], // right shift rounds down
  ["-7 / 2", Math.trunc(x / 2)], // integer division rounds towards zero
  ["-7 >>> 1 (32 bits)", x >>> 1], // zero-filling right shift
  ["1 << 31", 1 << 31],
  ["1 << 32", 1 << 32], // the count is taken modulo 32
];
for (const [label, value] of rows) console.log(`${label.padEnd(20)}${value}`);

Output

-7 >> 1             -4
-7 / 2              -3
-7 >>> 1 (32 bits)  2147483644
1 << 31             -2147483648
1 << 32             1

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node shifts.mjs

Java · compiled/Shifts.java

// Shifting a negative number, and shifting 1 by 31 and 32 places, in Java.
public class Shifts {
    static void row(String label, int value) {
        System.out.printf("%-20s%d%n", label, value);
    }

    public static void main(String[] args) {
        int x = -7, one = 1, count = 32;
        row("-7 >> 1", x >> 1); // right shift rounds down
        row("-7 / 2", x / 2); // integer division rounds towards zero
        row("-7 >>> 1 (32 bits)", x >>> 1); // zero-filling right shift
        row("1 << 31", one << 31);
        row("1 << 32", one << count); // an int shift uses only the low 5 bits of the count
    }
}

C++ · compiled/shifts.cpp

// Shifting a negative number, and shifting 1 by 31 places, in C++ (shift32.cpp tries 32).
#include <cstdint>
#include <print>

void row(const char* label, auto value) {
    std::println("{:<20}{}", label, value);
}

int main() {
    int x = -7, one = 1;
    row("-7 >> 1", x >> 1);  // right shift rounds down (defined since C++20)
    row("-7 / 2", x / 2);  // integer division rounds towards zero
    row("-7 >>> 1 (32 bits)", static_cast<std::uint32_t>(x) >> 1);  // no >>>: shift the unsigned value
    row("1 << 31", one << 31);  // the 1 moves into the sign bit: the lowest int
}

C++, by 32 · compiled/shift32.cpp

// Shifting a 32-bit int by 32 places: undefined behaviour in every version of C++.
#include <print>

int main() {
    int one = 1, count = 32;
    std::println("1 << 32 = {}", one << count);
}

Go · compiled/shifts.go

// Shifting a negative number, and shifting 1 by 31 and 32 places, in Go.
package main

import "fmt"

func row(label string, value any) {
	fmt.Printf("%-20s%v\n", label, value)
}

func main() {
	var x, one int32 = -7, 1
	count := 32
	row("-7 >> 1", x>>1)                    // right shift rounds down
	row("-7 / 2", x/2)                      // integer division rounds towards zero
	row("-7 >>> 1 (32 bits)", uint32(x)>>1) // no >>>: shift the unsigned value
	row("1 << 31", one<<31)                 // int32 wraps around
	row("1 << 32", one<<count)              // no limit on the count: every bit is shifted out
	row("1 << 32 as int", 1<<count)         // a plain int has 64 bits here
}
Running the Java, C++ and Go versions Python · compiled/shifts_session.py
from session import run, versions

# A line that starts with "!" must fail: shift32.cpp is built with -fno-sanitize-recover=undefined, so the
# sanitizer stops the program at the shift.
versions("java -version", "clang++ --version", "go version")
run("""
java Shifts.java
clang++ -std=c++23 -Wall -Wextra -fsanitize=address,undefined shifts.cpp -o shifts
./shifts
clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined -fno-sanitize-recover=undefined shift32.cpp -o shift32
!./shift32
go run shifts.go
""")

Output

# openjdk version "25.0.4.1" LTS
# Apple clang version 21.0.0 (clang-2100.3.34.2)
# go version go1.27.1 darwin/arm64
$ java Shifts.java
-7 >> 1             -4
-7 / 2              -3
-7 >>> 1 (32 bits)  2147483644
1 << 31             -2147483648
1 << 32             1
$ clang++ -std=c++23 -Wall -Wextra -fsanitize=address,undefined shifts.cpp -o shifts
$ ./shifts
-7 >> 1             -4
-7 / 2              -3
-7 >>> 1 (32 bits)  2147483644
1 << 31             -2147483648
$ clang++ -std=c++23 -Wall -Wextra -fsanitize=undefined -fno-sanitize-recover=undefined shift32.cpp -o shift32
$ ./shift32
shift32.cpp:6:38: runtime error: shift exponent 32 is too large for 32-bit type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior shift32.cpp:6:38 
[exit status 134]
$ go run shifts.go
-7 >> 1             -4
-7 / 2              -3
-7 >>> 1 (32 bits)  2147483644
1 << 31             -2147483648
1 << 32             0
1 << 32 as int      4294967296

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 shifts_session.py

The recorded outputs, row by row (Go’s values are for int32):

  • -7 >> 1: −4 in all five languages.
  • −7 divided by 2: −4 in Python, whose // rounds down; −3 in JavaScript, Java, C++ and Go.
  • -7 >>> 1 on 32 bits: 2147483644 in all five, although only JavaScript and Java have a >>> operator.
  • 1 << 31: 2147483648 in Python; −2147483648 in JavaScript, Java, C++ and Go.
  • 1 << 32: 4294967296 in Python; 1 in JavaScript and Java; 0 in Go; undefined behaviour in C++.

Right shifts of negative numbers round down in all five: -7 >> 1 is −4, because the sign bit is copied in from the left. Integer division rounds towards zero instead, so in JavaScript, Java, C++ and Go -7 / 2 is −3 and differs from -7 >> 1. Python’s // rounds down too, so there the two agree. Replace a division by a shift only when the value cannot be negative.

A shift that fills with zeros treats the number as unsigned. Java and JavaScript have an operator for it, >>>; in JavaScript it also converts its operand to an unsigned 32-bit number, which is why x >>> 0 is the usual way to read a value’s 32 bits as unsigned. C++ and Go have no >>>: shift an unsigned type, which their languages define as a logical shift. Python masks first: (x & 0xFFFFFFFF) >> 1.

Shifting by the full width gives three different answers, and none at all in C++:

  • JavaScript takes the shift count modulo 32, and Java uses only the five lowest bits of the count for an int (six for a long), so in both a shift by 32 is a shift by 0 and 1 << 32 is 1.
  • Go has no upper limit on the count: shifting an int32 32 places moves every bit out, and the result is 0. A plain Go int has 64 bits on 64-bit platforms such as this one, which is why the last row of the Go output is 4294967296.
  • In C++ a shift count that is negative, or not less than the width of the left operand, is undefined behaviour in every version of the language. Built with -fsanitize=undefined -fno-sanitize-recover=undefined, Clang’s sanitizer stops the program at that shift and names its line; without the sanitizer, the standard places no requirement on what the program does, so a wrong number is the mildest outcome.
  • Python’s integers have no width, so 1 << 32 is simply 2³².

Version note

C++20 made two’s complement the only representation of signed integers and defined both shifts on them: >> of a negative value rounds down, and << keeps the low bits of the exact result, so 1 << 31 on an int is the smallest int and -1 << 1 is −2. Before C++20, right-shifting a negative value was implementation-defined and left-shifting one was undefined. A shift count that is negative, or not less than the width, is undefined in every version.

The rule that keeps you safe in every language is short: keep the shift count below the width of the value you shift, and build masks wider than 32 bits from a 64-bit one: 1L << k in Java, 1ULL << k or std::uint64_t{1} << k in C++, a uint64 in Go, and BigInt values such as 1n << 40n in JavaScript, whose bitwise operators otherwise never go past 32 bits.

Precedence: parenthesise every bit expression

Two expressions that appear in real solutions, x & 1 == 0 (is x even?) and 1 << i + 1, are read differently from language to language. Predict what each version prints before you look:

Two unparenthesised bit expressions

Python · precedence/precedence.py

"""Two bit expressions without parentheses, as Python reads them."""

x, i = 6, 2
print("x & 1 == 0  ", x & 1 == 0)  # meant: is x even?
print("1 << i + 1  ", 1 << i + 1)  # meant: the mask of bit i + 1

Output

x & 1 == 0   True
1 << i + 1   8

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 precedence.py

JavaScript · precedence/precedence.mjs

// Two bit expressions without parentheses, as JavaScript reads them.
const x = 6, i = 2;
console.log(`x & 1 == 0   ${x & 1 == 0}`); // meant: is x even?
console.log(`1 << i + 1   ${1 << i + 1}`); // meant: the mask of bit i + 1

Output

x & 1 == 0   0
1 << i + 1   8

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node precedence.mjs

Java: AND · compiled/AndPrecedence.java

// x & 1 == 0 without parentheses, as Java reads it.
public class AndPrecedence {
    public static void main(String[] args) {
        int x = 6;
        System.out.println("x & 1 == 0   " + (x & 1 == 0)); // meant: is x even?
    }
}

Java: shift · compiled/ShiftPrecedence.java

// 1 << i + 1 without parentheses, as Java reads it.
public class ShiftPrecedence {
    public static void main(String[] args) {
        int i = 2;
        System.out.println("1 << i + 1   " + (1 << i + 1)); // meant: the mask of bit i + 1
    }
}

C++ · compiled/precedence.cpp

// Two bit expressions without parentheses, as C++ reads them.
#include <print>

int main() {
    int x = 6, i = 2;
    std::println("x & 1 == 0   {}", x & 1 == 0);  // meant: is x even?
    std::println("1 << i + 1   {}", 1 << i + 1);  // meant: the mask of bit i + 1
}

Go · compiled/precedence.go

// Two bit expressions without parentheses, as Go reads them.
package main

import "fmt"

func main() {
	x, i := 6, 2
	fmt.Println("x & 1 == 0  ", x&1 == 0) // meant: is x even?
	fmt.Println("1 << i + 1  ", 1<<i+1)   // meant: the mask of bit i + 1
}
Compiling and running the Java, C++ and Go versions Python · compiled/precedence_session.py
from session import run, versions

# A line that starts with "!" must fail: javac rejects AndPrecedence.java. -fno-caret-diagnostics keeps each
# compiler message on one line, without the source excerpt under it.
versions("java -version", "clang++ --version", "go version")
run("""
!java AndPrecedence.java
java ShiftPrecedence.java
clang++ -std=c++23 -Wall -Wextra -fno-caret-diagnostics precedence.cpp -o precedence
./precedence
go run precedence.go
""")

Output

# openjdk version "25.0.4.1" LTS
# Apple clang version 21.0.0 (clang-2100.3.34.2)
# go version go1.27.1 darwin/arm64
$ java AndPrecedence.java
AndPrecedence.java:5: error: bad operand types for binary operator '&'
        System.out.println("x & 1 == 0   " + (x & 1 == 0)); // meant: is x even?
                                                ^
  first type:  int
  second type: boolean
1 error
error: compilation failed
[exit status 1]
$ java ShiftPrecedence.java
1 << i + 1   8
$ clang++ -std=c++23 -Wall -Wextra -fno-caret-diagnostics precedence.cpp -o precedence
precedence.cpp:6:39: warning: & has lower precedence than ==; == will be evaluated first [-Wparentheses]
precedence.cpp:6:39: note: place parentheses around the '==' expression to silence this warning
precedence.cpp:6:39: note: place parentheses around the & expression to evaluate it first
precedence.cpp:7:44: warning: operator '<<' has lower precedence than '+'; '+' will be evaluated first [-Wshift-op-parentheses]
precedence.cpp:7:44: note: place parentheses around the '+' expression to silence this warning
$ ./precedence
x & 1 == 0   0
1 << i + 1   8
$ go run precedence.go
x & 1 == 0   true
1 << i + 1   5

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 precedence_session.py

With x = 6 and i = 2, the recorded outputs are:

  • x & 1 == 0: True in Python and true in Go; 0 in JavaScript; 0 in C++, with a warning; a compile error in Java.
  • 1 << i + 1: 8 in Python, JavaScript and Java; 8 in C++, with a warning; 5 in Go.

In JavaScript, Java and C++, == binds more tightly than &, so the first expression means x & (1 == 0). JavaScript turns false into 0 and quietly gives 0, which reads as “not even” for an even number. Java refuses to apply & to an int and a boolean, and C++ compiles it, with a warning from Clang. Python ranks comparisons below the bitwise operators, and Go puts & beside *, so both read (x & 1) == 0. The shift goes the other way: everywhere except Go, << binds more loosely than +, so 1 << i + 1 is 1 << 3; Go ranks << with * and reads (1 << i) + 1.

You do not need to remember five precedence tables. Put parentheses around every bitwise operation that sits next to a comparison or an arithmetic operator: (x & 1) == 0, (x & mask) != 0, 1 << (i + 1). Then every language reads the expression the way you meant it. The -fno-caret-diagnostics flag in the C++ command only keeps each compiler message on one line.

Binary strings and other bases

Turning a number into binary text, and back, is the last place where the five languages differ. A negative number is the test case that shows it:

−5 as binary text, and back

Python · bases/bases.py

"""Binary text in Python: signed digits, a 32-bit pattern, and the way back."""

x = -5
bits = format(x & 0xFFFFFFFF, "032b")  # the 32-bit pattern of x
u = int(bits, 2)  # int() reads the pattern as an unsigned number
rows = [
    ("bin(x)", bin(x)),
    ('format(x, "b")', format(x, "b")),
    ("32-bit pattern", bits),
    ("int(bits, 2)", u),
    ("back to signed", (u ^ 0x80000000) - 0x80000000),
    ('int("1011", 2)', int("1011", 2)),
    ('int("0b1011", 0)', int("0b1011", 0)),
    ("hex(255)", hex(255)),
]
for label, value in rows:
    print(f"{label:<18}{value}")

Output

bin(x)            -0b101
format(x, "b")    -101
32-bit pattern    11111111111111111111111111111011
int(bits, 2)      4294967291
back to signed    -5
int("1011", 2)    11
int("0b1011", 0)  11
hex(255)          0xff

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bases.py

JavaScript · bases/bases.mjs

// Binary text in JavaScript: signed digits, a 32-bit pattern, and the way back.
const x = -5;
const bits = (x >>> 0).toString(2).padStart(32, "0"); // the 32-bit pattern of x
const rows = [
  ["x.toString(2)", x.toString(2)],
  ["32-bit pattern", bits],
  ["parseInt(bits, 2)", parseInt(bits, 2)], // read as an unsigned number
  ["back to signed", parseInt(bits, 2) | 0], // | 0 converts to a signed 32-bit integer
  ['parseInt("1011", 2)', parseInt("1011", 2)],
  ['Number("0b1011")', Number("0b1011")],
  ["(255).toString(16)", (255).toString(16)],
];
for (const [label, value] of rows) console.log(`${label.padEnd(21)}${value}`);

Output

x.toString(2)        -101
32-bit pattern       11111111111111111111111111111011
parseInt(bits, 2)    4294967291
back to signed       -5
parseInt("1011", 2)  11
Number("0b1011")     11
(255).toString(16)   ff

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node bases.mjs

Java · compiled/Bases.java

// Binary text in Java: signed digits, a 32-bit pattern, and the way back.
public class Bases {
    static void row(String label, Object value) {
        System.out.printf("%-35s%s%n", label, value);
    }

    public static void main(String[] args) {
        int x = -5;
        String bits = Integer.toBinaryString(x); // 32 digits here; pad with zeros when bit 31 is 0
        row("Integer.toString(x, 2)", Integer.toString(x, 2));
        row("Integer.toBinaryString(x)", bits);
        try {
            row("Integer.parseInt(bits, 2)", Integer.parseInt(bits, 2));
        } catch (NumberFormatException e) {
            row("Integer.parseInt(bits, 2)", "NumberFormatException"); // 4294967291 does not fit an int
        }
        row("Integer.parseUnsignedInt(bits, 2)", Integer.parseUnsignedInt(bits, 2));
        row("Integer.parseInt(\"1011\", 2)", Integer.parseInt("1011", 2));
        row("Integer.toHexString(255)", Integer.toHexString(255));
    }
}

C++ · compiled/bases.cpp

// Binary text in C++: signed digits, a 32-bit pattern, and the way back.
#include <bitset>
#include <cstdint>
#include <format>
#include <print>
#include <stdexcept>
#include <string>

void row(const char* label, const auto& value) {
    std::println("{:<31}{}", label, value);
}

int main() {
    int x = -5;
    std::string bits = std::format("{:032b}", static_cast<std::uint32_t>(x));  // the 32-bit pattern of x
    row("std::format(\"{:b}\", x)", std::format("{:b}", x));
    row("32-bit pattern", bits);
    row("std::bitset<32>(x)", std::bitset<32>(x).to_string());
    try {
        row("std::stoi(bits, nullptr, 2)", std::stoi(bits, nullptr, 2));
    } catch (const std::out_of_range&) {
        row("std::stoi(bits, nullptr, 2)", "std::out_of_range");  // 4294967291 does not fit an int
    }
    row("back to signed", static_cast<std::int32_t>(std::stoul(bits, nullptr, 2)));
    row("std::stoi(\"1011\", nullptr, 2)", std::stoi("1011", nullptr, 2));
    row("std::format(\"{:x}\", 255)", std::format("{:x}", 255));
}

Go · compiled/bases.go

// Binary text in Go: signed digits, a 32-bit pattern, and the way back.
package main

import (
	"fmt"
	"strconv"
)

func row(label string, value any) {
	fmt.Printf("%-33s%v\n", label, value)
}

func main() {
	var x int32 = -5
	bits := fmt.Sprintf("%032b", uint32(x)) // the 32-bit pattern of x
	row(`fmt.Sprintf("%b", x)`, fmt.Sprintf("%b", x))
	row("32-bit pattern", bits)
	if _, err := strconv.ParseInt(bits, 2, 32); err != nil {
		row("strconv.ParseInt(bits, 2, 32)", err) // 4294967291 does not fit 32 signed bits
	}
	u, _ := strconv.ParseUint(bits, 2, 32)
	row("back to signed", int32(u))
	n, _ := strconv.ParseInt("1011", 2, 64)
	row(`strconv.ParseInt("1011", 2, 64)`, n)
	row("strconv.FormatInt(255, 16)", strconv.FormatInt(255, 16))
}
Running the Java, C++ and Go versions Python · compiled/bases_session.py
from session import run, versions

versions("java -version", "clang++ --version", "go version")
run("""
java Bases.java
clang++ -std=c++23 -Wall -Wextra -fsanitize=address,undefined bases.cpp -o bases
./bases
go run bases.go
""")

Output

# openjdk version "25.0.4.1" LTS
# Apple clang version 21.0.0 (clang-2100.3.34.2)
# go version go1.27.1 darwin/arm64
$ java Bases.java
Integer.toString(x, 2)             -101
Integer.toBinaryString(x)          11111111111111111111111111111011
Integer.parseInt(bits, 2)          NumberFormatException
Integer.parseUnsignedInt(bits, 2)  -5
Integer.parseInt("1011", 2)        11
Integer.toHexString(255)           ff
$ clang++ -std=c++23 -Wall -Wextra -fsanitize=address,undefined bases.cpp -o bases
$ ./bases
std::format("{:b}", x)         -101
32-bit pattern                 11111111111111111111111111111011
std::bitset<32>(x)             11111111111111111111111111111011
std::stoi(bits, nullptr, 2)    std::out_of_range
back to signed                 -5
std::stoi("1011", nullptr, 2)  11
std::format("{:x}", 255)       ff
$ go run bases.go
fmt.Sprintf("%b", x)             -101
32-bit pattern                   11111111111111111111111111111011
strconv.ParseInt(bits, 2, 32)    strconv.ParseInt: parsing "11111111111111111111111111111011": value out of range
back to signed                   -5
strconv.ParseInt("1011", 2, 64)  11
strconv.FormatInt(255, 16)       ff

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bases_session.py

  • Signed text. Python’s bin() and format(x, "b"), JavaScript’s toString(2), Java’s Integer.toString(x, 2), C++’s std::format("{:b}", x) and Go’s %b all write a minus sign and the magnitude: -101. Only bin() adds the 0b prefix.
  • The stored bits. Convert to an unsigned 32-bit value first and pad to 32 digits: format(x & 0xFFFFFFFF, "032b"), (x >>> 0).toString(2).padStart(32, "0"), std::format("{:032b}", static_cast<std::uint32_t>(x)) or fmt.Sprintf("%032b", uint32(x)). Java’s Integer.toBinaryString already writes the unsigned bits, but without leading zeros, so pad its result when bit 31 is off. C++’s std::bitset<32>(x).to_string() gives all 32 digits as well.
  • Parsing a pattern with bit 31 on. Python’s int(s, 2) and JavaScript’s parseInt(s, 2) read 11111111111111111111111111111011 as 4294967291. Java’s Integer.parseInt, C++’s std::stoi and Go’s strconv.ParseInt with 32 bits refuse it, because 4294967291 does not fit a signed 32-bit integer. Parse it as unsigned instead (Integer.parseUnsignedInt, std::stoul followed by a cast to std::int32_t, strconv.ParseUint followed by int32()), or apply the signed conversion yourself: | 0 in JavaScript, and in Python the same subtraction as to_int32.

Other bases work the same way. Pass 16 or 8 instead of 2 (int("ff", 16), parseInt("ff", 16), Integer.parseInt("ff", 16), strconv.ParseInt("ff", 16, 64)), and for output use hex(), toString(16), Integer.toHexString, std::format("{:x}", n) or strconv.FormatInt(n, 16). Python’s int(s, 0) and JavaScript’s Number(s) also accept a literal’s own prefix, as in 0b1011 or 0xff.

Programmer Calculator (Bitwise) Type an expression such as 1 << 31 or -7 >> 1 at a chosen width, signed or unsigned, and click bits to flip them. Number Base Converter Check a conversion between binary, hex and decimal, with the working shown.

Interview questions

How do you test, set, clear and toggle bit i of an integer x? (warm-up)

Build the mask 1 << i, which has only bit i on, and combine it with x. To test, compute x & (1 << i): it is not 0 exactly when the bit is on, so compare it with != 0, never with > 0, because for bit 31 of a signed 32-bit value the result is negative; (x >> i) & 1 gives the bit as 0 or 1. To set, use x | (1 << i); to clear, x & ~(1 << i), which ANDs with a mask that has every bit on except bit i (Go also has x &^ (1 << i)); to toggle, x ^ (1 << i). When x has 64 bits, build the mask from a 64-bit one, such as 1L << i in Java or 1ULL << i in C++, because a 32-bit 1 << 40 never reaches bit 40.

What is the difference between >> and >>> in Java and JavaScript? (warm-up)

>> is an arithmetic shift: it copies the sign bit into the places it frees on the left, so a negative number stays negative, and x >> k equals x divided by 2ᵏ, rounded down: -7 >> 1 is −4. >>> is a logical shift: it fills those places with zeros, so for any shift of at least one place the result is not negative: -7 >>> 1 is 2147483644 for a 32-bit value. In JavaScript >>> also converts its operand to an unsigned 32-bit number, so x >>> 0 reads a value’s bits as unsigned. Python, C++ and Go have no >>>: Python masks before shifting, and C++ and Go shift an unsigned type instead.

Key takeaways

  • x & (1 << i) tests bit i, x | (1 << i) sets it, x & ~(1 << i) clears it and x ^ (1 << i) toggles it, in all five languages; Go writes NOT as ^ and clears with &^.
  • Test bits with != 0, never > 0: in a signed 32-bit value, bit 31 makes the result negative.
  • Negative numbers are two’s complement everywhere. Python behaves as if it had infinitely many sign bits, so mask with & 0xFFFFFFFF to see 32 of them and subtract 2³² to read them as signed.
  • Right shifts of negative numbers round down; division by 2 rounds towards zero, except Python’s //.
  • Shifting a 32-bit value by 32 gives 1 in JavaScript and Java, 0 in Go and undefined behaviour in C++. Keep shift counts below the width, and build wide masks from a 64-bit or unsigned 1.
  • Parenthesise bit expressions next to comparisons and arithmetic: (x & 1) == 0, 1 << (i + 1).

Exercise

Exercise · Easy · Python

Apply set, clear and toggle steps to a 32-bit flag mask

Write flags_after(ops). A profile keeps its settings in one 32-bit mask that starts at 0, with every flag off. ops lists the steps to apply, in order, as pairs (action, bit) with a bit from 0 to 31: "set" turns that bit on, "clear" turns it off and "toggle" flips it. Return the final mask as a signed 32-bit integer, the value a Java int or a JavaScript bitwise result would hold, so a mask with bit 31 on is negative. Raise ValueError for any other action.

flags_after([("set", 0), ("set", 2), ("toggle", 2), ("set", 5)])  # 33: bits 0 and 5 are on
flags_after([("set", 31)])                                        # -2147483648
flags_after([("set", 31), ("set", 0), ("toggle", 1)])             # -2147483645
flags_after([])                                                   # 0

Clearing a bit that is already off changes nothing, and so does setting a bit that is already on.

Starter code · flags_after.py

def flags_after(ops):
    """The flag mask after applying ops to a mask that starts at 0, as a signed 32-bit integer.

    ops is a list of (action, bit) pairs: action is "set", "clear" or "toggle", and bit is 0 to 31.
    """
    # Replace this line with your code.
    return 0
The sample tests · test_flags_after.py
import random

from flags_after import flags_after


def test_examples():
    """the examples of the prompt"""
    assert flags_after([("set", 0), ("set", 2), ("toggle", 2), ("set", 5)]) == 33
    assert flags_after([("set", 31)]) == -2147483648
    assert flags_after([("set", 31), ("set", 0), ("toggle", 1)]) == -2147483645
    assert flags_after([]) == 0


def test_repeated_steps():
    """clearing a bit that is off and setting a bit that is on change nothing"""
    assert flags_after([("clear", 7)]) == 0
    assert flags_after([("set", 4), ("set", 4)]) == 16
    assert flags_after([("set", 4), ("clear", 4), ("clear", 4)]) == 0


def test_toggle_twice():
    """two toggles of the same bit cancel out"""
    assert flags_after([("toggle", 9), ("toggle", 9)]) == 0
    assert flags_after([("set", 3), ("toggle", 31), ("toggle", 31)]) == 8


def test_bit_31():
    """bit 31 is the sign bit of a signed 32-bit integer"""
    assert flags_after([("toggle", 31)]) == -2147483648
    assert flags_after([("set", 31), ("clear", 31), ("set", 30)]) == 1073741824
    assert flags_after([("set", b) for b in range(32)]) == -1
    assert flags_after([("set", b) for b in range(31)]) == 2147483647


def test_against_a_list_of_bits():
    """300 random lists of steps, compared with a slow check that keeps 32 booleans"""
    rng = random.Random(31)
    for _ in range(300):
        ops = [(rng.choice(["set", "clear", "toggle"]), rng.choice([0, 1, 15, 30, 31, rng.randrange(32)])) for _ in range(rng.randint(0, 25))]
        on = [False] * 32
        for action, bit in ops:
            if action == "set":
                on[bit] = True
            elif action == "clear":
                on[bit] = False
            else:
                on[bit] = not on[bit]
        expected = sum(1 << b for b in range(31) if on[b]) - (1 << 31 if on[31] else 0)
        assert flags_after(ops) == expected, f"flags_after({ops!r})"


def test_unknown_action():
    """an action other than set, clear or toggle raises ValueError"""
    try:
        flags_after([("flip", 3)])
    except ValueError:
        return
    raise AssertionError("expected ValueError for the action 'flip'")
A hint

Keep the mask as an ordinary Python integer while you apply the steps: mask |= 1 << bit sets a bit, mask &= ~(1 << bit) clears it and mask ^= 1 << bit toggles it. Only at the end turn the mask into a signed 32-bit value: keep its low 32 bits with & 0xFFFFFFFF, and if bit 31 of that number is 1, the signed value is 2³² less.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 Which expression clears bit 4 of x and leaves every other bit as it was?

    Choose one answer.

    Show the answer to question 1

    Answer: x & ~(1 << 4)

    ~(1 << 4) has every bit on except bit 4, so ANDing with it turns bit 4 off and keeps the rest. XOR toggles bit 4, so it would turn the bit on when it was off; the OR sets every bit except bit 4; and x & (1 << 4) keeps only bit 4, which is the test, not the clear.

  2. Question 2 of 6 In JavaScript, flags has bit 31 on. Which condition is true?

    Choose one answer.

    Show the answer to question 2

    Answer: (flags & (1 << 31)) !== 0

    Bitwise results in JavaScript are signed 32-bit integers, and bit 31 is the sign bit: flags & (1 << 31) is −2147483648, which is not 0 but is less than 0, and so is flags itself. >> copies the sign bit, so flags >> 31 is −1; (flags >>> 31) === 1 would be true.

  3. Question 3 of 6 Before you look at its recorded output: what does precedence.mjs print?

    What does this program print? Choose one answer.

    // Two bit expressions without parentheses, as JavaScript reads them.
    const x = 6, i = 2;
    console.log(`x & 1 == 0   ${x & 1 == 0}`); // meant: is x even?
    console.log(`1 << i + 1   ${1 << i + 1}`); // meant: the mask of bit i + 1
    Show the answer to question 3

    Answer: it prints

    x & 1 == 0   0
    1 << i + 1   8

    In JavaScript == binds more tightly than &, so the first expression is 6 & (1 == 0), that is 6 & false, which converts false to 0 and gives 0. << binds more loosely than +, so the second is 1 << 3, which is 8. Python gives True and 8 for the same text, and Go gives true and 5.

  4. Question 4 of 6 A 32-bit signed 1 is shifted left by a variable that holds 32. In which languages is the result 1?

    Choose every answer that is right.

    Show the answer to question 4

    Answer:

    • JavaScript
    • Java

    JavaScript takes the shift count modulo 32 and Java uses its low 5 bits, so a count of 32 shifts by 0 and the result is 1. Go has no limit on the count, so every bit is shifted out and the int32 result is 0. In C++ a count that is not less than the width is undefined behaviour, which the sanitizer reports. Python's integers have no width: 1 << 32 is 4294967296.

  5. Question 5 of 6 In Python, what is -6 & 0xFF?

    Type a number.

    Show the answer to question 5

    Answer: 250

    In 8 bits, −6 is stored as 256 − 6 = 250, which is 1111 1010. & 0xFF keeps exactly those 8 bits of Python's endless two's complement row, so the result is 250.

  6. Question 6 of 6 Which Python expression gives the 32-bit pattern of −5, 11111111111111111111111111111011?

    Choose one answer.

    Show the answer to question 6

    Answer: format(-5 & 0xFFFFFFFF, "032b")

    -5 & 0xFFFFFFFF is 4294967291, the number whose 32 binary digits are the pattern. format(-5, "032b") writes a minus sign and the magnitude, -0000…0101; slicing bin(-5) keeps only the magnitude, 000…0101; and bin(~5) is '-0b110'.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.