Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jakelevi1996/40b4788232e392020fcdd97ffed788c8 to your computer and use it in GitHub Desktop.
Save jakelevi1996/40b4788232e392020fcdd97ffed788c8 to your computer and use it in GitHub Desktop.
Approximate log2(x) using integer arithmetic

Approximate log2(x) using integer arithmetic

TODO 2020-07-17: tidy up this Gist. Binary search for size in bits adapted from source code for WebRTC project.

import numpy as np
import matplotlib.pyplot as plt

def log2Q10_xQ12(x_Q12):
    """
    Using the approximation log2(1 + x) ~= x for 1 <= x < 2:

    - | x_frac = x_Q12 & 0xFFF
    - | x = (2 ** -lshifts) * (2 ** 12 + x_frac)
    - | => log2(x) = -lshifts + log2(2 ** 12 + x_frac)
    - | = -lshifts + log2(2 ** 12 * (1 + x_frac * 2 ** -12))
    - | = -lshifts + 12 + log2(1 + x_frac * 2 ** -12)
    - | ~= -lshifts + 12 + x_frac * 2 ** -12
    - | In Q10:
    - | = ((-lshifts + 12) << 10) + (x_frac >> 2)
    """
    lshifts = 12
    while x_Q12 >= (1 << 13):
        x_Q12 >>= 1
        lshifts -= 1
    while x_Q12 < (1 << 12):
        x_Q12 <<= 1
        lshifts += 1
    x_frac = x_Q12 & ((1 << 12) - 1)
    # x_frac =  x_Q12 - (1 << 12)
    return ((-lshifts + 12) << 10) + (x_frac >> 2)

log2 = lambda x: log2Q10_xQ12(int(x * (1 << 12))) / (1 << 10)

def get_size_in_bits_bs(x):
    """
    Returns the number of bits that are needed at the most to represent x using
    a binary search. In a valid range:

    WebRtcSpl_GetSizeInBits(n) == int(np.log2(n)) + 1
    """

    if (0xFFFF0000 & x):
        bits = 16
    else:
        bits = 0
  
    if (0x0000FF00 & (x >> bits)): bits += 8
    if (0x000000F0 & (x >> bits)): bits += 4
    if (0x0000000C & (x >> bits)): bits += 2
    if (0x00000002 & (x >> bits)): bits += 1
    if (0x00000001 & (x >> bits)): bits += 1

    return bits

def plot_data(x, y1, y2, name="plot"):
    plt.figure(figsize=[8, 6])
    plt.plot(x, y1, "r")
    plt.plot(x, y2, "b")
    plt.grid(True)
    plt.title(name)
    plt.tight_layout()
    plt.savefig(name)
    plt.close()

# x = np.arange(0.1, 4.1, 0.1)
# x = np.arange(1, 2.1, 0.1)
x = np.arange(0.1, 32.1, 0.1)
y1 = np.log2(x)
y2 = [log2(xi) for xi in x]
plot_data(x, y1, y2, "log2 approximation")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment