You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Sad Catapella N1][Numerical correctness] Float histogram binning loses finite near-zero values due to scale overflow/underflow #27
Normalization Batch 1 at f784c95e3049483f87466150ffc5764375fa631d. Confirmed floating-point arithmetic edge case by source inspection and isolated formula probe, not a package benchmark or test run.
Evidence
_float_bins, lines 657–679 computes half_range = hi*0.5 - lo*0.5, then (n / half_range) and multiplies it by (value*0.5 - lo*0.5) before clipping. When hi-lo is a tiny but nonzero finite float64 range, the first factor can become inf or half_range can underflow to zero, despite hi != lo.
An array np.array([0., 1e-308], dtype=np.float64) with 100 bins has half_range=5e-309; the ratio 100 / half_range overflows to infinity, so the bin-index computation can yield NaN/infinity and cast to a sentinel integer before clipping. The two distinct finite values can therefore collapse to bin 0 instead of first and last. For np.nextafter(0.,1.), halving the difference becomes zero. Isolated NumPy arithmetic confirmed this behavior.
Impact
Incorrect histogram counts/range checks for valid subnormal or near-zero floating data without an explicit refusal. Rare in normal raw microscopy but plausible in transformed scientific arrays; the library advertises float64 support without an exclusion.
Fix / acceptance
Develop a scale-safe normalized coordinate calculation that avoids both overflow of n/range and underflow of intermediate halves, retaining established bin boundary/rounding conventions for normal values. Do not add an arbitrary epsilon or coerce small numbers to zero. Compare with an exact/high-precision independent oracle for subnormals, 1e-300, mixed signs, extreme opposite signs, constant planes, and robust-mode coincident limits. Ensure min/max, counts, quantiles, warnings, error bounds and block-vs-whole agreement. Benchmark the stable method before replacing hot loops.
This does not reproduce on mainc952263 (v0.4.0). It was fixed by #85/#87: float_bin_position uses a bounded fraction wherever halving would underflow or n / half_range would overflow.
I checked it again on c952263 for the review of #91:
[0, 1e-308] with 100 bins gives bins 0 and 99. [0, 5e-324] with 2 and 100 bins gives the first and last bin. [-1e-300, 1e-300] with 64 bins gives bins 0 and 63. [0, 1e-310] with 7 bins gives bins 0 and 6. [-5e-324, 5e-324] with 3 bins gives bins 0 and 2. A constant 1e-300 plane gives bin 0 only. Every representative is finite.
I compared against an exact Fraction oracle, floor((v - lo) / (hi - lo) * n), using 20,000 uniform values at the scales 1e-308, 1e-300, 1e-320, 7·5e-324 and 1e-310. I used 2, 3, 16, 100 and 257 bins, min-max and robust ranges, and read the data in two blocks through histogram_with_range_check. All 50 cases match bin for bin.
test_float_range_of_the_smallest_subnormal_keeps_distinct_bins, test_float_narrow_range_does_not_overflow_the_bin_scale and test_float_range_between_subnormals_keeps_every_pixel_in_its_bin cover these cases as regression tests.
Activity
guijacquemet commentedon Nov 11, 2026
This does not reproduce on
mainc952263 (v0.4.0). It was fixed by #85/#87:float_bin_positionuses a bounded fraction wherever halving would underflow orn / half_rangewould overflow.I checked it again on c952263 for the review of #91:
[0, 1e-308]with 100 bins gives bins 0 and 99.[0, 5e-324]with 2 and 100 bins gives the first and last bin.[-1e-300, 1e-300]with 64 bins gives bins 0 and 63.[0, 1e-310]with 7 bins gives bins 0 and 6.[-5e-324, 5e-324]with 3 bins gives bins 0 and 2. A constant1e-300plane gives bin 0 only. Every representative is finite.Fractionoracle,floor((v - lo) / (hi - lo) * n), using 20,000 uniform values at the scales 1e-308, 1e-300, 1e-320, 7·5e-324 and 1e-310. I used 2, 3, 16, 100 and 257 bins, min-max and robust ranges, and read the data in two blocks throughhistogram_with_range_check. All 50 cases match bin for bin.test_float_range_of_the_smallest_subnormal_keeps_distinct_bins,test_float_narrow_range_does_not_overflow_the_bin_scaleandtest_float_range_between_subnormals_keeps_every_pixel_in_its_bincover these cases as regression tests.I'm closing this as completed by #85/#87.
Generated by Claude Code