fix(webui): compact-format precision bugs (review IMPORTANT-2/3)

formatCompactCount: Math.floor(scaled*factor)/factor under-reported by
one unit whenever the intermediate scaled=abs/divisor value was not
exactly representable in binary floating point (8_700_000 -> "8.69M",
not "8.7M" — 8.7 itself has no exact double representation, so the
original two-step division/floor landed on 869 instead of 870).
Reordered to floor(abs*factor/divisor) — multiplying the original
INTEGER first keeps every intermediate value exact for any realistic
count, since abs*factor stays within Number.MAX_SAFE_INTEGER. New rows:
8_700_000 -> "8.7M", 1_130_000 -> "1.13M". (review IMPORTANT-2)

formatContextWindow: always divided by 1024 (binary-K), which is right
for a genuinely binary context size (262144 -> "256k") but wrong for a
decimal, marketing-style round count (128000 -> "125k", an odd number
nobody would recognize as "128K"). A plain "tokens % 1024 == 0" check
cannot separate these — 128000 is ALSO an exact multiple of 1024 (125 x
1024) despite not being a power of two. The actual, consistent
discriminator is POWER OF TWO: 262144 = 2^18, 128000 = 2^10 x 5^3 (not a
power of two). Binary-K/M now applies only when tokens is an exact power
of two; decimal-K/M otherwise. Fixed rows: 128000 -> "128k" (was
"125k"), 1_000_000 -> "1M" (was "977k"), 2_000_000 -> "2M" (was
"1953k"); 262144 -> "256k" and 32768 -> "32k" unchanged. (review
IMPORTANT-3)
This commit is contained in:
2026-08-22 02:12:54 +01:00
parent e3996d88bf
commit 5e3cf919d9
2 changed files with 92 additions and 18 deletions
+29 -6
View File
@@ -54,15 +54,31 @@ describe('refreshDetailLabel', () => {
// cost). formatContextWindow is table-driven over good/edge cases;
// formatModelCostHover covers the free/priced split ModelChip's
// hover-detail relies on.
//
// Review fix: binary-K/M (÷1024) applies ONLY when tokens is an exact
// power of two; a decimal, round-thousands count uses decimal-K/M
// (÷1000) instead — see formatContextWindow's own doc comment for why a
// plain "tokens % 1024 == 0" check cannot tell these two cases apart
// (128000 divides evenly by 1024 too, yet must render as decimal "128k",
// not binary "125k").
describe('formatContextWindow', () => {
it.each([
{ tokens: 262144, want: '256k' },
{ tokens: 32768, want: '32k' },
{ tokens: 1_000_000, want: '977k' },
{ tokens: 8192, want: '8k' },
{ tokens: 512, want: '512' }, // under 1024: plain digits, no "k"
// Genuine binary sizes (exact powers of two) — binary-K/M.
{ tokens: 262144, want: '256k' }, // 2^18 — LM Studio's own worked example
{ tokens: 131072, want: '128k' }, // 2^17
{ tokens: 32768, want: '32k' }, // 2^15
{ tokens: 8192, want: '8k' }, // 2^13
{ tokens: 1024, want: '1k' }, // 2^10 — boundary
{ tokens: 1_048_576, want: '1M' }, // 2^20 — binary M-tier boundary
// Decimal, marketing-style round counts — NOT powers of two, even
// when they happen to be exact multiples of 1024 (128000 = 125 ×
// 1024) — decimal-K/M.
{ tokens: 128_000, want: '128k' }, // review regression: was "125k"
{ tokens: 1_000_000, want: '1M' }, // review regression: was "977k"
{ tokens: 2_000_000, want: '2M' }, // review regression: was "1953k"
// Plain digits under 1024, either path.
{ tokens: 512, want: '512' },
{ tokens: 0, want: '0' },
{ tokens: 1024, want: '1k' }, // boundary
])('formatContextWindow($tokens) = $want', ({ tokens, want }) => {
expect(formatContextWindow(tokens)).toBe(want)
})
@@ -105,6 +121,13 @@ describe('formatCompactCount', () => {
{ n: 1_234_567, want: '1.23M' },
{ n: 45_600_000, want: '45.6M' },
{ n: 999_999_999, want: '999M' }, // just under the 1e9 boundary
// Review fix: IEEE-754 precision regressions. 8_700_000 / 1_000_000
// is not exactly representable in binary floating point (the double
// closest to 8.7 sits a hair BELOW it); the old
// `Math.floor(scaled*factor)/factor` implementation floored that
// slightly-under value to 869 instead of 870, rendering "8.69M".
{ n: 8_700_000, want: '8.7M' },
{ n: 1_130_000, want: '1.13M' },
// B tier.
{ n: 1_000_000_000, want: '1B' }, // the 1e9 boundary itself
{ n: 1_230_000_000, want: '1.23B' },