From 8136d4d19cd7be9b70735f8cfdd3f35c961ae0be Mon Sep 17 00:00:00 2001 From: mmporong Date: Sat, 31 Oct 2026 23:34:26 +0900 Subject: [PATCH] =?UTF-8?q?fix(math):=202=EC=9D=98=20=EA=B1=B0=EB=93=AD?= =?UTF-8?q?=EC=A0=9C=EA=B3=B1=20=EC=9E=85=EB=A0=A5=EC=9D=84=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/melonjs/CHANGELOG.md | 1 + packages/melonjs/src/math/math.ts | 6 +++++- packages/melonjs/tests/math.spec.js | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/melonjs/CHANGELOG.md b/packages/melonjs/CHANGELOG.md index fa37f15a7d..69c3d7f137 100644 --- a/packages/melonjs/CHANGELOG.md +++ b/packages/melonjs/CHANGELOG.md @@ -23,6 +23,7 @@ - UI: `UITextButton#bitmapText`, for `label` ### Fixed +- Math: `isPowerOfTwo()` rejects zero, negative and fractional values instead of accepting them after 32-bit coercion, and handles powers and non-powers beyond the 32-bit range (thanks @mmporong) - Input: a child cropped out of view by a container with `clipping`, or scrolled out of a `UIPanel`, no longer gets pointer events where it is not drawn - UI: a `UITextButton` moved after it was built draws its background where it is, not where it was first put - UI: a blocking `UIPanel` stays opaque to hover for as long as the pointer is on it, not only on the frame the pointer enters diff --git a/packages/melonjs/src/math/math.ts b/packages/melonjs/src/math/math.ts index 26dbdecde8..fa0ff085f6 100644 --- a/packages/melonjs/src/math/math.ts +++ b/packages/melonjs/src/math/math.ts @@ -34,7 +34,11 @@ export const EPSILON = 0.000001; * @returns true if number is a power of two */ export function isPowerOfTwo(val: number) { - return (val & (val - 1)) === 0; + if (!Number.isInteger(val) || val <= 0) { + return false; + } + const exponent = Math.log2(val); + return Number.isInteger(exponent) && val === 2 ** exponent; } /** diff --git a/packages/melonjs/tests/math.spec.js b/packages/melonjs/tests/math.spec.js index e54cef701b..3eb362abaf 100644 --- a/packages/melonjs/tests/math.spec.js +++ b/packages/melonjs/tests/math.spec.js @@ -328,6 +328,27 @@ describe("Math", () => { it("32 is a Power of 2", () => { expect(math.isPowerOfTwo(32)).toEqual(true); }); + it("0 and negative values are not Powers of 2", () => { + expect(math.isPowerOfTwo(0)).toEqual(false); + expect(math.isPowerOfTwo(-2)).toEqual(false); + }); + it("fractional values are not Powers of 2", () => { + expect(math.isPowerOfTwo(2.5)).toEqual(false); + }); + it("supports Powers of 2 outside the 32-bit range", () => { + expect(math.isPowerOfTwo(2 ** 40)).toEqual(true); + expect(math.isPowerOfTwo(3 * 2 ** 32)).toEqual(false); + }); + it("rejects large non-Powers whose logarithms round to integers", () => { + for (const value of [ + Number.MAX_SAFE_INTEGER, + 2 ** 53 + 2, + 2 ** 53 + 4, + 2 ** 54 - 2, + ]) { + expect(math.isPowerOfTwo(value)).toEqual(false); + } + }); it("1027 is not a Power of 2", () => { expect(math.isPowerOfTwo(1027)).toEqual(false); });