binary mathematics
power of two
mathematical algorithms
number theory
computational mathematics

Previous power of 2

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Finding the previous power of two means computing the largest value 2^k that is less than or equal to a given positive integer. This operation appears in memory sizing, hash-table growth policies, networking buffers, and low-level optimization code. Several approaches exist, and the best one depends on readability, performance, and language features.

Core Sections

Define the Problem Precisely

Given integer n where n > 0, return:

  • '1 if n is 1'
  • otherwise the largest power of two not greater than n

Examples:

  • 'n = 1 returns 1'
  • 'n = 7 returns 4'
  • 'n = 16 returns 16'
  • 'n = 31 returns 16'

Always define behavior for zero and negative inputs up front.

Simple Loop Method

A straightforward and readable method repeatedly doubles until next value would exceed n.

python
1def previous_power_of_two_loop(n: int) -> int:
2    if n <= 0:
3        raise ValueError("n must be positive")
4    p = 1
5    while (p << 1) <= n:
6        p <<= 1
7    return p
8
9for x in [1, 2, 3, 7, 8, 9, 31, 32, 33]:
10    print(x, previous_power_of_two_loop(x))

This method is easy to audit and works in any language.

Bit Length Method in Python

Python offers bit_length, which makes a compact solution:

python
1def previous_power_of_two_bit_length(n: int) -> int:
2    if n <= 0:
3        raise ValueError("n must be positive")
4    return 1 << (n.bit_length() - 1)
5
6print(previous_power_of_two_bit_length(31))  # 16
7print(previous_power_of_two_bit_length(32))  # 32

This is often the cleanest Python implementation.

Bit-Twiddling Approach in C-like Languages

For fixed-width integers, you can propagate highest set bit and then isolate it.

c
1#include <stdint.h>
2
3uint32_t previous_power_of_two(uint32_t n) {
4    if (n == 0) return 0;
5    n |= (n >> 1);
6    n |= (n >> 2);
7    n |= (n >> 4);
8    n |= (n >> 8);
9    n |= (n >> 16);
10    return n - (n >> 1);
11}

This runs in constant time for fixed-width integers and is common in performance-critical code.

Java Implementation

Java has built-in helpers that simplify this task.

java
1public class PrevPowerOfTwo {
2    static int previousPowerOfTwo(int n) {
3        if (n <= 0) throw new IllegalArgumentException("n must be positive");
4        return Integer.highestOneBit(n);
5    }
6
7    public static void main(String[] args) {
8        System.out.println(previousPowerOfTwo(31)); // 16
9        System.out.println(previousPowerOfTwo(32)); // 32
10    }
11}

highestOneBit is exactly the operation needed.

Distinguish Previous Power Versus Next Power

Developers sometimes mix these operations:

  • previous power of two: <= n
  • next power of two: >= n

They solve different problems. For capacity planning, you may need next power. For bucketing and floor alignment, you may need previous power.

Handling Zero and Negative Values

Mathematically, powers of two are positive in this context. Common engineering choices:

  • raise error for n <= 0
  • return 0 sentinel for n == 0 in systems code

Pick one policy and keep it consistent across your API.

Practical Use Cases

Common examples:

  1. selecting bucket width in histogram logic
  2. aligning allocation blocks to powers of two
  3. choosing FFT window sizes and bounds
  4. reducing search space in bit-based algorithms

The function itself is tiny, but incorrect edge handling can create downstream bugs.

Quick Test Set

For confidence, test exact powers and neighbors:

python
1tests = {
2    1: 1,
3    2: 2,
4    3: 2,
5    4: 4,
6    5: 4,
7    15: 8,
8    16: 16,
9    17: 16,
10}
11
12for n, expected in tests.items():
13    got = previous_power_of_two_bit_length(n)
14    assert got == expected, (n, got, expected)

These cases catch most logic errors quickly.

Common Pitfalls

  • Not defining behavior for zero and negative values.
  • Confusing previous power operation with next power operation.
  • Using floating-point log2 solutions that can fail on large integers due to precision.
  • Forgetting integer width limits in C and similar languages.
  • Skipping tests around exact power boundaries such as 8, 16, and 32.

Summary

  • Previous power of two means largest 2^k such that result is not greater than input.
  • Use simple loops for readability or bit operations for fixed-width performance.
  • In Python, bit_length gives a concise and robust implementation.
  • Handle edge cases for zero and negative inputs explicitly.
  • Test power boundaries and neighbor values to ensure correctness.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.