From c26cc0350bfde58d227cf7507b675abcc306aeea Mon Sep 17 00:00:00 2001 From: Lumi Pakkanen Date: Sat, 13 Jan 2024 12:59:08 +0200 Subject: [PATCH] Implement ceiling power of two --- src/__tests__/index.spec.ts | 27 +++++++++++++++++++++++++++ src/index.ts | 15 +++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/__tests__/index.spec.ts b/src/__tests__/index.spec.ts index 005b406..05c3407 100644 --- a/src/__tests__/index.spec.ts +++ b/src/__tests__/index.spec.ts @@ -2,6 +2,7 @@ import {describe, it, expect} from 'vitest'; import { arraysEqual, binomial, + ceilPow2, circleDifference, circleDistance, clamp, @@ -152,3 +153,29 @@ describe('Pitch distance with circle equivalence', () => { expect(diff).toBeCloseTo(247.741); }); }); + +describe('Ceiling power of two', () => { + it('works with small values', () => { + const x = 1 + Math.random() * (2 ** 30 - 2); + const p2 = ceilPow2(x); + expect(x).toBeLessThanOrEqual(p2); + expect(p2).toBeLessThan(2 * x); + expect(Math.log2(p2)).toBeCloseTo(Math.round(Math.log2(p2))); + }); + + it('works with tiny values', () => { + const x = Math.random(); + const p2 = ceilPow2(x); + expect(x).toBeLessThanOrEqual(p2); + expect(p2).toBeLessThan(2 * x); + expect(Math.log2(p2)).toBeCloseTo(Math.round(Math.log2(p2))); + }); + + it('works with large values', () => { + const x = 2 ** 31 + Math.random() * 2 ** 37; + const p2 = ceilPow2(x); + expect(x).toBeLessThanOrEqual(p2); + expect(p2).toBeLessThan(2 * x); + expect(Math.log2(p2)).toBeCloseTo(Math.round(Math.log2(p2))); + }); +}); diff --git a/src/index.ts b/src/index.ts index 794f255..93a7849 100644 --- a/src/index.ts +++ b/src/index.ts @@ -284,3 +284,18 @@ export function circleDifference(a: number, b: number, equaveCents = 1200.0) { export function circleDistance(a: number, b: number, equaveCents = 1200.0) { return Math.abs(circleDifference(a, b, equaveCents)); } + +/** + * Calculate the smallest power of two greater or equal to the input value. + * @param x Value to compare to. + * @returns Smallest `2**n` such that `x <= 2**n`. + */ +export function ceilPow2(x: number) { + if (x >= 1 && x < 0x40000000) { + return 1 << (32 - Math.clz32(x - 1)); + } + if (x <= 0) { + return 0; + } + return 2 ** Math.ceil(Math.log2(x)); +}