fix(tools): h5rs diff compares integers exactly under -d/-p

With a tolerance, integers were converted to f64 before comparing, so
int64/uint64 values above 2^53 that differ compared equal: -d 0 on 2^60
and 2^60 + 1 exited 0, where h5diff exits 1. Integer pairs are now
compared in i128 (the delta against floor(D), the relative quotient from
an exact difference), and the report prints the exact difference.

h5diff compares exactly when -p is below the f64 epsilon (2^60 and
2^60 + 1 differ at -p 1e-18, and nextafter(2, 0) and 2 at -p 1.5e-16);
h5rs now does the same.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-26 01:16:09 -05:00
co-authored by Claude Opus 5.5
parent 699ee9c447
commit f325d111f3
5 changed files with 102 additions and 10 deletions
+6
View File
@@ -126,6 +126,12 @@ for name, last in (("copied.h5", 1), ("copied_changed.h5", 9)):
g["d"] = np.arange(3)
g.create_group("s")["e"] = np.array([0, last if gname == "h" else 1])
# 64-bit integers one apart, beyond f64's 2^53 integer precision.
for name, d in (("big1.h5", 0), ("big2.h5", 1)):
with h5py.File(os.path.join(out, name), "w") as f:
f["i"] = np.array([2**60 + d, -(2**62) - d], dtype="i8")
f["u"] = np.array([2**64 - 1 - d], dtype="u8")
with h5py.File(os.path.join(out, "userblock.h5"), "w", userblock_size=1024) as f:
f["d"] = np.arange(10)
@@ -380,6 +380,38 @@ fn diff_exit_codes_match_h5diff() {
assert!(s.contains("2 difference(s) found"), "{s}");
}
/// Tolerances on 64-bit integers one apart, far beyond f64's integer
/// precision: compared exactly, as h5diff does.
#[test]
fn diff_tolerances_compare_large_integers_exactly() {
let Some(f) = generate() else { return };
let (a, b) = (f.p("big1.h5"), f.p("big2.h5"));
let cases: Vec<(Vec<&str>, i32)> = vec![
(vec![&a, &b], 1),
(vec!["-d", "0", &a, &b], 1),
(vec!["-d", "0.5", &a, &b], 1),
(vec!["-d", "1", &a, &b], 0),
(vec!["-d", "0", &a, &b, "/u"], 1),
(vec!["-p", "0", &a, &b], 1),
(vec!["-p", "1e-18", &a, &b, "/i"], 1),
(vec!["-p", "1e-15", &a, &b], 0),
];
let have_h5diff = tool_available("h5diff");
for (args, want) in cases {
if have_h5diff {
assert_eq!(code(&run("h5diff", &args)), want, "h5diff {args:?}");
}
let o = h5rs(&[&["diff"], args.as_slice()].concat());
assert_eq!(code(&o), want, "h5rs diff {args:?}: {}", stdout(&o));
}
// The reported difference is exact too.
let s = stdout(&h5rs(&["diff", "-r", "-d", "0", &a, &b, "/u"]));
assert!(
s.contains("18446744073709551615 18446744073709551614 1\n"),
"{s}"
);
}
/// An object hard-linked under two names is compared under both, with
/// everything below it, so a file that shares one object between two names
/// equals a file that stores two identical copies.