Skip to content

hrv: state the real pct overflow bound, and pin that the wrap could land positive - #1688

Merged
ryanbr merged 1 commit into
mainfrom
hrv-pct-overflow-bound
Aug 27, 2026
Merged

hrv: state the real pct overflow bound, and pin that the wrap could land positive#1688
ryanbr merged 1 commit into
mainfrom
hrv-pct-overflow-bound

Conversation

@ryanbr

@ryanbr ryanbr commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Follow-up to #1685, which widened HrvAnalyzer.pct correctly. Two things about the doc comments it
added are worth getting exactly right, because they are what the next person reasons from.

1. The quoted bound is the wrong one, and it understates the exposure

Both comments say the overflow starts above 2147483647 / 200 = 10,737,418 ms. That is the bound for
part * 200 in isolation — but the numerator is part * 200 + total. Since part <= total, it
first exceeds Int.MAX_VALUE at total >= 10,683,999 (2147483647 / 201): about 53,000 ms lower,
and 2.97 h rather than 2.98 h.

10,737,418 is also the last value that does not overflow even for the multiply alone — that
starts at 10,737,419.

2. The wrap did not always land negative — this is the half that matters

pct(28_800_000, 28_800_000)   32-bit -> 25      correct = 100

A 100% multi-share 8 h night returned 25: positive, plausible, and wrong by 75 points.

So a negative multiMs was a symptom of this bug and never a test for it. Anyone re-reading
pre-fix Android captures for #1008 / #1118 / #1331 / #1451 cannot filter on the sign — every Android
multiMs past the bound is suspect, including the ones that read fine. That is a stronger claim
than the original made, and it changes how the existing logs should be treated.

What changed

Comments corrected on both platforms, plus a twin test pinning the exact boundary and the
readable-wrong case:

pct(10_683_998, 10_683_998)   32-bit ->  100    <- last one 32 bits still got right
pct(10_683_999, 10_683_999)   32-bit -> -100    <- first that wrapped
pct(28_800_000, 28_800_000)   32-bit ->   25    <- wrapped, but not to anything negative

No behaviour change: pct itself is untouched, and all three assertions pass on both platforms today.
They are a parity pin and a guard for arm64_32, where the Kotlin failure reproduces exactly.

How it was verified

Two independently written models of 32-bit truncation — one in Swift via the standalone oracle
(swiftc -O twin.swift main.swift), one in Python — agree on all five values. Both also reproduce the
-16 that @Zebsi235's real 08-22 and 08-26 nights printed, which is what makes them worth trusting for
the 25.

The new test was falsified before being trusted. With pct reverted to Int arithmetic:

pctWrapWasNotAlwaysNegative FAILED
java.lang.AssertionError: expected:<100> but was:<-100>

on the 10,683,999 case — a value the original comment described as safe. That is the correction,
executable. Fix restored: 10 tests, 0 failures, on freshly cleared result XMLs.

python3 Tools/doc_comment_lint.py — OK, no new detached doc comments.

swift test not run locally (StrandAnalytics needs macOS via GRDB); swift-packages.yml covers it,
and the asserted values come from the oracle rather than from reading the code.

Refs #1685. Docs and tests only — the arithmetic landed there and is unchanged here.

…and positive

Follow-up to #1685, which widened HrvAnalyzer.pct correctly. Two things about it
are worth getting exactly right, because the doc comments it added are what the
next person will reason from.

THE QUOTED BOUND IS THE WRONG ONE, AND IT UNDERSTATES THE EXPOSURE. Both comments
say the overflow starts above 2147483647 / 200 = 10,737,418 ms. That is the bound
for `part * 200` in isolation, but the numerator is `part * 200 + total`. Since
part <= total, the numerator first exceeds Int.MAX_VALUE at total >= 10,683,999
(2147483647 / 201), about 53,000 ms lower and 2.97 h rather than 2.98 h.
10,737,418 is also the last value that does NOT overflow even for the multiply
alone; that starts at 10,737,419.

THE WRAP DID NOT ALWAYS LAND NEGATIVE. This is the half that matters. A 100%
multi-share 8 h night, pct(28_800_000, 28_800_000), returned 25 in 32 bits:
positive, plausible, and wrong by 75 points. So a negative multiMs was a symptom
of the bug and never a test for it, and anyone re-reading pre-fix Android
captures for #1008 / #1118 / #1331 / #1451 cannot filter on the sign. Every
Android multiMs past the bound is suspect, including the ones that read fine.
That is a stronger claim than the one the original made, and it changes how the
existing logs should be treated.

Comments on both platforms corrected, plus a twin test pinning the exact boundary
and the readable-wrong case: 10,683,998 was still computed correctly in 32 bits,
10,683,999 wrapped to -100, 28,800,000 wrapped to 25.

Verified rather than reasoned. Two independently written models of 32-bit
truncation - one in Swift via the standalone oracle, one in Python - agree on all
five values, and both reproduce the -16 the submitter's real 08-22 and 08-26
nights printed, which is what makes them worth trusting for the 25.

The new test was falsified before being trusted: with pct reverted to Int
arithmetic it fails expected:<100> but was:<-100> on the 10,683,999 case - a
value the original comment described as safe, which is the correction, executable.
Restored, 10 tests 0 failures, on freshly cleared result XMLs.

python3 Tools/doc_comment_lint.py: OK, no new detached doc comments.
@ryanbr
ryanbr merged commit 4c7b1b5 into main Aug 27, 2026
14 checks passed
@ryanbr
ryanbr deleted the hrv-pct-overflow-bound branch August 27, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant