Fix silent wrong data and libhdf5 interop found by the HDF5 audit #11
@@ -246,8 +246,20 @@ fn scaleoffset_decompress(
|
||||
fill_value
|
||||
} else if is_escale {
|
||||
minval + code as f64 * powi_f64(2.0, scale_factor)
|
||||
} else if elem_size == 4 {
|
||||
// H5Z_scaleoffset_modify_3/4 for `float`: the code is
|
||||
// read as an `int` and everything is single precision,
|
||||
// `(float)code / powf(10, D) + min`. Doing it in f64 and
|
||||
// rounding once at the end is off by 1 ULP at times.
|
||||
let d = if scale_factor >= 0 {
|
||||
powi_f64(10.0, scale_factor) as f32
|
||||
} else {
|
||||
minval + code as f64 / powi_f64(10.0, scale_factor)
|
||||
1.0 / powi_f64(10.0, -scale_factor) as f32
|
||||
};
|
||||
((code as u32 as i32) as f32 / d + minval as f32) as f64
|
||||
} else {
|
||||
// ... and for `double`: `(double)(long)code / pow(10, D) + min`.
|
||||
(code as i64) as f64 / powi_f64(10.0, scale_factor) + minval
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@@ -1630,6 +1642,46 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// `le_data.h5` scale-offset (D-scale, D = 3, fill -2.2) chunks, decoded
|
||||
/// bit for bit as libhdf5 does: single-precision arithmetic for `float`
|
||||
/// (we computed in f64 and rounded once, which was 1 ULP off for e.g.
|
||||
/// 1.6663333: `694ad53f` instead of `6a4ad53f`), double for `double`.
|
||||
#[test]
|
||||
fn scaleoffset_float_dscale_matches_libhdf5_bits() {
|
||||
let file: &[u8] = include_bytes!("../tests/fixtures/filters/le_data.h5");
|
||||
let cd = |size: u32, order: u32, fill_lo: u32, fill_hi: u32| {
|
||||
let mut cd = vec![0, 3, 12, 1, size, 0, order, 1, fill_lo, fill_hi];
|
||||
cd.resize(20, 0);
|
||||
cd
|
||||
};
|
||||
let f32_le = cd(4, 0, 0xC00C_CCCD, 0);
|
||||
let f32_be = cd(4, 1, 0xC00C_CCCD, 0);
|
||||
let f64_le = cd(8, 0, 2576980378, 3221330329);
|
||||
#[rustfmt::skip]
|
||||
let cases: [(usize, usize, &[u32], &str); 6] = [
|
||||
(2816, 38, &f32_le, "abaaaa3ed2942a3fec0a803fd2942a3fec0a803fabaaaa3fec0a803fabaaaa3f694ad53fabaaaa3f694ad53f76050040"),
|
||||
(2854, 38, &f32_le, "abaaaa3f6a4ad53f760500406a4ad53f7605004056551540760500405655154034a52a405655154034a52a4076054040"),
|
||||
(712, 38, &f32_be, "3eaaaaab3f2a94d23f800aec3f2a94d23f800aec3faaaaab3f800aec3faaaaab3fd54a693faaaaab3fd54a6940000576"),
|
||||
(750, 38, &f32_be, "3faaaaab3fd54a6a400005763fd54a6a40000576401555564000057640155556402aa53440155556402aa53440400576"),
|
||||
(2050, 38, &f64_le, concat!(
|
||||
"555555555555d53fb9d75c489a52e53fce3e7c865d01f03fb9d75c489a52e53fce3e7c865d01f03f555555555555f53f",
|
||||
"ce3e7c865d01f03f555555555555f53fdc6b2e244da9fa3f555555555555f53fdc6b2e244da9fa3f671f3ec3ae000040")),
|
||||
(2088, 38, &f64_le, concat!(
|
||||
"555555555555f53fdc6b2e244da9fa3f671f3ec3ae000040dc6b2e244da9fa3f671f3ec3ae000040aaaaaaaaaaaa0240",
|
||||
"671f3ec3ae000040aaaaaaaaaaaa0240ee351792a6540540aaaaaaaaaaaa0240ee351792a6540540671f3ec3ae000840")),
|
||||
];
|
||||
for (off, len, cd, want) in cases {
|
||||
let pipeline = FilterPipeline {
|
||||
version: 2,
|
||||
filters: vec![one_filter(FILTER_SCALEOFFSET, cd.to_vec())],
|
||||
};
|
||||
let want = unhex(want);
|
||||
let got =
|
||||
decompress_chunk(&file[off..off + len], &pipeline, want.len(), cd[4]).unwrap();
|
||||
assert_eq!(got, want, "chunk at {off}");
|
||||
}
|
||||
}
|
||||
|
||||
/// `le_data.h5` `/Nbit_float_data_{le,be}` chunk (0,0): a 20-bit float
|
||||
/// (offset 7) packed by N-Bit. The filter must reproduce libhdf5's
|
||||
/// decoded bytes in the *file* datatype (h5py `DatasetID.read` with the
|
||||
|
||||
Reference in New Issue
Block a user