Skip to content

Fix histogram bin redistribution dropping counts at the range boundary#7139

Open
Osamaali313 wants to merge 1 commit into
tensorflow:masterfrom
Osamaali313:fix/histogram-boundary-bin-count-loss
Open

Fix histogram bin redistribution dropping counts at the range boundary#7139
Osamaali313 wants to merge 1 commit into
tensorflow:masterfrom
Osamaali313:fix/histogram-boundary-bin-count-loss

Conversation

@Osamaali313

Copy link
Copy Markdown

Problem

buildNormalizedHistograms (in tensorboard/webapp/widgets/histogram/histogram_util.ts) redistributes input bins into binCount equal-width result bins. In rebuildBins, the last result bin's right edge was computed as:

const resultLeft = left + i * dx;
const resultRight = resultLeft + dx;   // == left + (binCount-1)*dx + dx

Because dx = (right - left) / binCount is generally not exactly representable, this sum accumulates floating-point error and can land just below range.right. For range = [0, 1] with binCount = 6, the last resultRight is 0.9999999999999999.

An input bin whose right edge sits exactly on range.right (for example a zero-width bin at the maximum value, or the final normal bin's edge) then satisfies the inner-loop guard bin.x + bin.dx > resultRight (1 > 0.9999999999999999), so the loop breaks without consuming that bin — and its y counts are silently dropped from the normalized histogram shown to the user.

Minimal reproduction (through the public API):

buildNormalizedHistograms(
  [{step: 0, wallTime: 0, bins: [{x: 0, dx: 1, y: 1}, {x: 1, dx: 0, y: 5}]}],
  6,
)
// total y in output: 1  (expected 6 — the 5 counts on the x=1 bin vanish)

Fix

The last result bin must extend exactly to range.right, which is by construction the true upper bound of all input data. Clamp it:

const resultLeft = left + i * dx;
const isLastResultBin = i === binCount - 1;
const resultRight = isLastResultBin ? right : resultLeft + dx;

This is a general fix (not a special-case hack) and leaves all interior bins unchanged. After it, the reproduction above conserves all 6 counts, and normal inputs are unaffected.

Tests

Added a count conservation at the range boundary block to histogram_util_test.ts asserting that total counts are preserved when a bin's right edge sits on range.right (both a zero-width boundary bin and a final normal bin). These fail on the current code and pass with the fix.

@google-cla

google-cla Bot commented Jul 1, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

buildNormalizedHistograms redistributes input bins into binCount
equal-width result bins. The last result bin's right edge was computed
as resultLeft + dx == left + (binCount-1)*dx + dx, which accumulates
floating-point error and can land just below range.right (e.g. for
range [0, 1] with binCount 6 it is 0.9999999999999999). An input bin
whose right edge sits exactly on range.right then satisfies
'bin.x + bin.dx > resultRight' in the inner loop and the loop breaks
without consuming it, so its y counts are silently dropped from the
normalized histogram.

Clamp the last result bin's right edge to range.right (its true upper
bound by construction). Add regression tests asserting count
conservation when a bin's right edge sits on range.right.
@Osamaali313 Osamaali313 force-pushed the fix/histogram-boundary-bin-count-loss branch from a9f7e6e to bc7926a Compare July 1, 2026 17:22
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