Repository navigation
fix: BigInt inputs lose precision and misdetect unit boundaries #354
Description
Summary
BigInt inputs lose precision and misdetect unit boundaries because filesize() coerces the argument with Number(arg), which silently rounds values above Number.MAX_SAFE_INTEGER (2^53 - 1). Users passing bigint values get inaccurate results.
Reproduction
- Call
filesize(BigInt(2 ** 53 + 1))— the+1is silently dropped. - Call
filesize(BigInt(10 ** 24 - 1))— a value just below 1 YB. - Call
filesize(BigInt(1024 ** 8 - 1), { standard: "iec" })— a value just below 1 YiB.
Expected Behavior
filesize(BigInt(2 ** 53 + 1))should produce a distinct, accurate result fromfilesize(BigInt(2 ** 53))— the+1must not be lost.filesize(BigInt(10 ** 24 - 1))should report the value in ZB (exponent 7), not YB (exponent 8).filesize(BigInt(1024 ** 8 - 1), { standard: "iec" })should report ZiB (exponent 7), not YiB (exponent 8).
Actual Behavior
filesize(BigInt(2 ** 53 + 1))andfilesize(BigInt(2 ** 53))both return"9.01 PB"— the+1is lost.filesize(BigInt(10 ** 24 - 1))returns"1 YB"(exponent 8) — the value is rounded up across the boundary.filesize(BigInt(1024 ** 8 - 1), { standard: "iec" })returns"1 YiB"(exponent 8) — same boundary misdetection.
Environment
- Node.js version: v25.8.1
- OS: Linux 7.0.14-17-pve
- filesize.js version: 11.0.24
Code Sample
import { filesize } from "filesize";
filesize(BigInt(2 ** 53 + 1)); // "9.01 PB" — should differ from 2^53
filesize(BigInt(10 ** 24 - 1)); // "1 YB" — should be "1000 ZB"
filesize(BigInt(1024 ** 8 - 1), { standard: "iec" }); // "1 YiB" — should be "1024 ZiB"Additional Context
The root cause is the single coercion num = Number(arg) in src/filesize.js. Number() cannot represent integers above 2^53 exactly, so any bigint above that threshold loses precision. This also affects exponent detection: Number(10 ** 24 - 1) rounds up to 10 ** 24, crossing the YB boundary and producing the wrong unit.
The fix is a BigInt-specific branch that performs exponent detection and value division using bigint arithmetic (avoiding Number() until the final division), then rejoins the common output path. The unit ceiling is YB (exponent 8) for SI and YiB (exponent 8) for IEC.
Audit Findings (for Issue #354)
- File:
src/filesize.js— line 89num = Number(arg)is the single root cause.Number()cannot represent integers above 2^53 exactly, so anybigintaboveNumber.MAX_SAFE_INTEGERloses precision. src/filesize.js—calculateExponent()(line 129) andcalculateOptimizedValue()(line 139) both operate on the coercednum. They useMath.log()and float division, which cannot recover the lost precision.src/helpers.js—calculateExponent()usesMath.log(num) / LOG_10_1000(orLOG_2_1024), andcalculateOptimizedValue()dividesnumbyDECIMAL_POWERS[e]/BINARY_POWERS[e]. Both are float paths.src/constants.js—BINARY_POWERSandDECIMAL_POWERSare arrays ofnumber(max 2^80 / 10^24). The ceiling is exponent 8 (YB / YiB), matching the user's stated limit.
Fix Steps
- Detect BigInt input — In
filesize(), checktypeof arg === "bigint"before theNumber(arg)coercion at line 89. Route bigint inputs to a dedicated branch. - Add a BigInt exponent path — Compute the exponent with
bigintcomparisons (e.g.,num >= 10n ** BigInt(3 * (e + 1))for SI,num >= 1024n ** BigInt(e + 1)for IEC), clamped to exponent 8. This avoidsMath.log()precision loss. - Add a BigInt value path — Divide the
bigintby the appropriatebigintpower (e.g.,10n ** BigInt(3 * e)or1024n ** BigInt(e)) using a scaled division that preserves precision (e.g.,Number((num * 10n ** 16n) / power) / Number(10n ** 16n)), then apply the existing bits/auto-increment logic. - Rejoin the common path — After computing
valueande, feed the same values into the existingapplyRounding,applyPrecisionHandling,decorateResult, andformatOutputflow. No overlap in the BigInt branch until the returns. - Add regression tests — In
tests/unit/filesize.test.js, add cases forBigInt(2 ** 53 + 1)(must differ from2 ** 53),BigInt(10 ** 24 - 1)(must be ZB, not YB), andBigInt(1024 ** 8 - 1)withstandard: "iec"(must be ZiB, not YiB). - Verify — Run
npm testandnpm run coverageto confirm no regressions and 100% coverage maintained.
Activity
fix: preserve BigInt precision above Number.MAX_SAFE_INTEGER
avoidwork commentedon Sep 27, 2026
Fixed in #355.