Skip to content

fix(h5): keep non-uniform float32 image axes with a large origin - #18

Merged
PierreRaybaut merged 1 commit into
developfrom
fix/h5-uniform-axis-precision
Oct 24, 2026
Merged

PierreRaybaut merged 1 commit into
developfrom
fix/h5-uniform-axis-precision

Conversation

@PierreRaybaut

Copy link
Copy Markdown
Contributor

Problem

_is_uniform() (introduced in 589aafa, part of #13, unreleased 1.1.0) accepted a deviation from even spacing of 4 * eps * max|x|, i.e. 4 to 8 ULP of the axis data type. A genuinely non-uniform float32 axis with a large origin was therefore flattened to uniform coordinates, silently altering the spatial calibration of imported NeXus images and mapped (HDF5 profile) images.

Reproduced case: float32 axis [1e6, 1e6 + 1, 1e6 + 2.25, 1e6 + 3] (steps 1, 1.25, 0.75; deviation 4 ULP = 25 % of the step) was imported as uniform, the third coordinate becoming 1e6 + 2.

Fix

  • An image gets uniform coordinates only if both axes deviate from even spacing by at most 2.5 ULP of their stored data type (sigimax.h5.nexus.UNIFORM_AXIS_TOLERANCE), evaluated at the largest axis magnitude (float64 ULP for non-float axes). Otherwise the stored coordinates are kept exactly.
  • The maximum deviation of each declared axis converted to uniform coordinates is recorded in experimental_source conversions, e.g. axis 'x': uniform coordinates (max deviation 0.000195, 0.4 ULP of float32). Lossless conversions (zero deviation, dimensions without axis) are not recorded.
  • Non-finite axis values still lead to explicit coordinates (NaN-safe comparison).

Rationale for 2.5 ULP

Measured on 3000 random evenly spaced float32 axes: linspace, decimation and bin edges → centers deviate by at most 1.0 ULP; float32 arithmetic (x0 + i * dx) by at most 2.1 ULP. A step-relative guard (e.g. ≤ 1 % of the step) was considered and rejected: it would have turned about 15 % of genuinely uniform float32 axes (small step relative to the origin, e.g. 8000 eV / 0.1 eV) into non-uniform coordinates, which Sigima binning and resampling refuse.

Known limit: a genuinely non-uniform axis deviating by ≤ 2.5 ULP of its storage type cannot be distinguished from a uniform axis computed in that type; it is converted, but its deviation is now recorded.

Tests

test_nxdata_image_axis_precision:

  • reproduced case → explicit coordinates, stored values preserved exactly, nothing recorded;
  • float32 linspace(8000, 8100, 1001) → uniform, deviation recorded (0.4 ULP);
  • float32 arithmetic axis -50 + i * 0.37 (1.87 ULP) → uniform, guarding against an over-tight criterion.

The new test fails on the previous implementation. sigimax/tests/hdf5: 309 passed; Ruff and Pylint clean; Sphinx gettext build with -W OK.

- NeXus and mapped image axes were converted to uniform coordinates when
  they deviated from even spacing by up to 4 * eps * max|x|, i.e. 4 to 8
  ULP of their data type: a float32 axis such as [1e6, 1e6 + 1,
  1e6 + 2.25, 1e6 + 3] was imported with 1e6 + 2 as third coordinate.
  Axes are now converted only if they deviate by at most 2.5 ULP of their
  stored data type (UNIFORM_AXIS_TOLERANCE), evenly spaced values computed
  in their storage precision deviating by up to about 2 ULP; other axes
  keep their stored coordinates exactly.
- The maximum deviation of each axis converted to uniform coordinates is
  recorded among the experimental_source conversions.
- Documented the criterion in the HDF5 guide, the API reference and the
  release notes, with French translations.

Assisted-by: Claude Opus 5.5
@PierreRaybaut
PierreRaybaut merged commit d7d716f into develop Oct 24, 2026
8 checks passed
@PierreRaybaut
PierreRaybaut deleted the fix/h5-uniform-axis-precision branch October 24, 2026 05:43
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