Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion tensorboard/webapp/widgets/histogram/histogram_util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,14 @@ function rebuildBins(bins: Bin[], range: Range, binCount: number): Bin[] {
let nextBinContribution = 0;
for (let i = 0; i < binCount; i++) {
const resultLeft = left + i * dx;
const resultRight = resultLeft + dx;
const isLastResultBin = i === binCount - 1;
// The last result bin must extend exactly to `range.right`. Computing it as
// `resultLeft + dx` accumulates floating-point error in the repeated `i * dx`
// sum and can land just below `range.right`; an input bin whose right edge
// sits exactly on `range.right` then satisfies `bin.x + bin.dx > resultRight`
// and is skipped without being consumed, dropping its counts. Clamp the last
// result bin's right edge to `range.right` so those counts are preserved.
const resultRight = isLastResultBin ? right : resultLeft + dx;

let resultY = nextBinContribution;
nextBinContribution = 0;
Expand Down
32 changes: 32 additions & 0 deletions tensorboard/webapp/widgets/histogram/histogram_util_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,38 @@ describe('histogram util', () => {
});
});

describe('count conservation at the range boundary', () => {
function totalCount(bins: Bin[]): number {
return bins.reduce((sum, bin) => sum + bin.y, 0);
}

it('preserves a bin whose right edge sits on range.right', () => {
// A zero-width bin at x=1 sits exactly on the range's right edge (the
// widest histogram spans [0, 1]). It must not be dropped when the bins
// are redistributed into `binCount` result bins.
const input: Bin[] = [
{x: 0, dx: 1, y: 1},
{x: 1, dx: 0, y: 5},
];
const [result] = histogramsToBins(
buildNormalizedHistograms([binsToHistogram(input)], 6)
);
expect(totalCount(result)).toBeCloseTo(totalCount(input), 6);
});

it('preserves counts when the final normal bin ends on range.right', () => {
const input: Bin[] = [
{x: 0, dx: 1, y: 2},
{x: 1, dx: 1, y: 3},
{x: 2, dx: 1, y: 4},
];
const [result] = histogramsToBins(
buildNormalizedHistograms([binsToHistogram(input)], 7)
);
expect(totalCount(result)).toBeCloseTo(totalCount(input), 6);
});
});

describe('single histogram', () => {
it('converts a 0 width bin into a default bin', () => {
expect(
Expand Down