Python math.ldexp(): Fast Multiplication by Powers of Two

The math.ldexp(x, i) function computes x × 2i (multiplying a number by 2 raised to an integer power). It is the exact mathematical inverse of math.frexp(). Instead of performing expensive exponentiation or repeated multiplication, math.ldexp() directly manipulates the binary exponent of IEEE 754 floating-point numbers at the C standard library level, making it exceptionally fast and bit-exact.

Exact Inverse: If m, e = math.frexp(val), then math.ldexp(m, e) == val reconstructing the original float with zero loss of precision.

Syntax & Parameters 🔝

The math.ldexp() function belongs to Python's built-in math module:

import math

math.ldexp(x, i)
Parameter Type Description
x float or int Required. The multiplicand (significand / mantissa). Can be positive, negative, or zero.
i int Required. The integer exponent of 2. Passing a float raises a TypeError.
Return Value float The result of x × 2i as a 64-bit IEEE 754 floating-point number.
TypeError Exception Raised when i is a float (e.g. math.ldexp(2.0, 3.5)).
OverflowError Exception Raised when the calculated magnitude exceeds the maximum representable float (~1.7977 × 10308).

Basic Usage & Scaling Examples 🔝

Whether shifting values upwards with positive exponents or scaling down with negative exponents, math.ldexp() returns an exact float:

import math

# Multiplying by positive powers of 2 (Left shift scaling)
print("ldexp(3.0, 4):  ", math.ldexp(3.0, 4))    # 3.0 * (2**4) = 3.0 * 16 = 48.0
print("ldexp(1.5, 3):  ", math.ldexp(1.5, 3))    # 1.5 * 8 = 12.0

# Multiplying by negative powers of 2 (Division by powers of 2)
print("ldexp(10.0, -2):", math.ldexp(10.0, -2))  # 10.0 / 4 = 2.5
print("ldexp(1.0, -3): ", math.ldexp(1.0, -3))   # 1.0 / 8 = 0.125

# Exponent of 0 returns the input unchanged as a float
print("ldexp(7.25, 0): ", math.ldexp(7.25, 0))   # 7.25 * 1 = 7.25

# Preserving sign and negative zero
print("ldexp(-2.5, 2): ", math.ldexp(-2.5, 2))   # -2.5 * 4 = -10.0
print("ldexp(-0.0, 5): ", math.ldexp(-0.0, 5))   # Preserves signed -0.0

Output:

ldexp(3.0, 4):   48.0
ldexp(1.5, 3):   12.0
ldexp(10.0, -2): 2.5
ldexp(1.0, -3):  0.125
ldexp(7.25, 0):  7.25
ldexp(-2.5, 2):  -10.0
ldexp(-0.0, 5):  -0.0

Inverse Relationship with math.frexp() 🔝

The companion function math.frexp(x) decomposes any real number x into a fractional mantissa m and integer exponent e such that 0.5 ≤ |m| < 1.0 and x = m × 2e. math.ldexp(m, e) reconstructs the exact original value:

import math

test_values = [0.0, 0.125, 1.0, 13.375, 1024.0, -88.5, 1e15]

print(f"{'Original (x)':>14} | {'Mantissa (m)':>12} | {'Exp (e)':>7} | {'Reconstructed':>14} | Match?")
print("-" * 64)

for orig in test_values:
    m, e = math.frexp(orig)
    reconstructed = math.ldexp(m, e)
    matches = (reconstructed == orig)
    print(f"{orig:14.3f} | {m:12.6f} | {e:7d} | {reconstructed:14.3f} | {matches}")

Output:

  Original (x) | Mantissa (m) | Exp (e) |  Reconstructed | Match?
----------------------------------------------------------------
         0.000 |     0.000000 |       0 |          0.000 | True
         0.125 |     0.500000 |      -2 |          0.125 | True
         1.000 |     0.500000 |       1 |          1.000 | True
        13.375 |     0.835938 |       4 |         13.375 | True
      1024.000 |     0.500000 |      11 |       1024.000 | True
       -88.500 |    -0.691406 |       7 |        -88.500 | True
 1000000000000000.000 |     0.888178 |      50 | 1000000000000000.000 | True

Performance & IEEE 754 Binary Exponent Modification 🔝

Under the IEEE 754 double-precision standard, a 64-bit float comprises three fields:

  • 1 Sign bit
  • 11 Exponent bits (biased by 1023)
  • 52 Fraction/Mantissa bits

When you execute x * (2 ** i) or x * math.pow(2, i), Python must compute the power of 2 (allocating a large integer or evaluating a floating-point power routine) and then perform a standard floating-point multiplication. In contrast, math.ldexp(x, i) delegates directly to the standard C library function ldexp(), which simply adds i directly to the float's 11-bit exponent register:

Approach How It Works Rounding & Bit-Exactness
math.ldexp(x, i) Adds i directly to the float exponent field Bit-exact. Zero intermediate rounding errors.
x * (2 ** i) Calculates integer power of 2, then multiplies Exact for small i, but allocates Python integers.
x * math.pow(2, i) Evaluates exp(i * ln(2)), then multiplies Subject to potential floating-point rounding jitter.

Error Handling & Overflow/Underflow Boundaries 🔝

Because math.ldexp() operates strictly on binary exponent bitfields, it enforces strict type and range constraints:

1. Second Argument Must Be An Integer (TypeError)

Passing a floating-point number as the exponent i raises a TypeError:

import math

try:
    math.ldexp(4.0, 2.5)  # Exponent must be integer!
except TypeError as err:
    print("TypeError caught:", err)

Output:

TypeError caught: 'float' object cannot be interpreted as an integer

2. Exponent Overflow (OverflowError)

The maximum exponent supported by IEEE 754 64-bit floats is +1023. If x × 2i exceeds the maximum floating-point value, Python raises an OverflowError:

import math

try:
    math.ldexp(1.0, 1024)
except OverflowError as err:
    print("OverflowError caught:", err)

Output:

OverflowError caught: math range error

3. Graceful Underflow to Zero

Conversely, when i is extremely negative (e.g. i ≤ -1075), the result gracefully underflows past subnormal float precision and returns 0.0 without raising an error:

import math

print("ldexp(1.0, -1074):", math.ldexp(1.0, -1074))  # Smallest subnormal float: 5e-324
print("ldexp(1.0, -1075):", math.ldexp(1.0, -1075))  # Underflows to 0.0

Hands-on Exercise: DSP Audio Fixed-Point Normalizer with Assertions 🔝

In Digital Signal Processing (DSP) and audio engineering, 16-bit Pulse Code Modulation (PCM) audio samples are stored as signed 16-bit integers ranging from -32,768 to +32,767. To apply digital filters, audio algorithms require samples normalized to the floating-point range [-1.0, 1.0].

Because 16-bit audio uses a 15-bit amplitude magnitude (with 1 bit for sign), scaling to float is performed by multiplying by 2−15 via math.ldexp(sample, -15). Conversely, converting normalized float audio back to 16-bit integer PCM is achieved by scaling by 215 via math.ldexp(val, 15):

Python Exercise: Audio PCM16 to Float Normalizer
import math

def pcm16_to_float(pcm_val):
    """Converts 16-bit signed integer PCM (-32768 to 32767) to normalized float [-1.0, 1.0]."""
    if not isinstance(pcm_val, int):
        raise TypeError("PCM sample must be an integer.")
    if pcm_val < -32768 or pcm_val > 32767:
        raise ValueError("PCM sample out of 16-bit signed range [-32768, 32767].")
    return round(math.ldexp(pcm_val, -15), 6)

def float_to_pcm16(float_val):
    """Converts normalized float audio [-1.0, 1.0] back to 16-bit signed integer PCM."""
    if not isinstance(float_val, (int, float)):
        raise TypeError("Normalized sample must be numeric.")
    clamped = max(-1.0, min(1.0, float(float_val)))
    scaled = math.ldexp(clamped, 15)
    return int(round(scaled))

# Unit Assertions
assert pcm16_to_float(0) == 0.0
assert pcm16_to_float(32767) == 0.999969
assert pcm16_to_float(-32768) == -1.0
assert pcm16_to_float(16384) == 0.5

assert float_to_pcm16(0.0) == 0
assert float_to_pcm16(0.5) == 16384
assert float_to_pcm16(-1.0) == -32768

# Boundary and type checking assertions
try:
    pcm16_to_float(40000)
    assert False, "Should raise ValueError"
except ValueError:
    pass

raw_audio = [-32768, -16384, 0, 16384, 32767]
normalized = [pcm16_to_float(s) for s in raw_audio]
print("Raw 16-bit PCM: ", raw_audio)
print("Normalized float:", normalized)
print("All DSP audio conversion assertions passed successfully!")

Practice in Google Colab & GitHub 🔝

Open in Google Colab View on GitHub

Run all math.ldexp() scaling examples, test round-trip reconstruction with math.frexp(), inspect floating-point boundary behavior, and execute DSP fixed-point audio conversions in Google Colab.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer