From ffc3fd8edd7f151fb183af764aa279f925d814de Mon Sep 17 00:00:00 2001 From: Scot Breitenfeld Date: Fri, 28 Aug 2026 12:49:23 -0500 Subject: [PATCH] Align the H5Zdevelop.h get_config contract with the RFC Three corrections to the public plugin-author documentation, which had drifted from what the library actually does and, in one case, advised the opposite of what the format requires. The note on reconstructing floats recommended %a, on the grounds that a hex literal round-trips through strtod with no rounding. That is true, and it is why %a remains accepted on input to set_config, but get_config output must be valid TOML and a hexadecimal float literal is not. A plugin author following the note would emit a string no non-HDF5 reader could parse. Replaced with the %.16e requirement and the reasons for rejecting %a, %g, %f, and any width outside [17, DECIMAL_DIG]. Added a second note that recovering a value from a decimal literal depends on the reader's decimal-to-binary conversion being correctly rounded -- only recommended by C11 7.22.1.3p11, in contrast to p9, which requires it for the hexadecimal form. A 1 ulp difference changes behaviour in a filter that quantises on a binary boundary. Also: H5Pget_filter_params_by_idx returns the stored string in canonical form, not "verbatim" -- outer braces are stripped and hex-float literals rewritten -- and DBL_DECIMAL_DIG is C11, not C99. C99 had only DECIMAL_DIG. --- src/H5Zconfig.c | 2 +- src/H5Zdevelop.h | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/H5Zconfig.c b/src/H5Zconfig.c index 3cf277cbbc5..d418694b1ea 100644 --- a/src/H5Zconfig.c +++ b/src/H5Zconfig.c @@ -187,7 +187,7 @@ H5Z__rewrite_hexfloats(const char *src) * contains a decimal point and exponent, so * tomlc17 parses it as TOML_FP64 not TOML_INTEGER. * 17 significant digits guarantee IEEE 754 - * double round-trip fidelity (C99 DBL_DECIMAL_DIG). */ + * double round-trip fidelity (C11 DBL_DECIMAL_DIG). */ char dec[32]; int n = snprintf(dec, sizeof(dec), "%.16e", val); /* LC_NUMERIC may replace '.' with the locale decimal diff --git a/src/H5Zdevelop.h b/src/H5Zdevelop.h index edf95933afd..134d7e6449a 100644 --- a/src/H5Zdevelop.h +++ b/src/H5Zdevelop.h @@ -234,14 +234,33 @@ typedef herr_t (*H5Z_set_config_func_t)(const char *params, unsigned *flags, siz * * \details This callback is only a fallback for introspection: when a filter * was configured with a parameter string, H5Pget_filter_params_by_idx() - * returns that stored string verbatim and never calls get_config. + * returns that stored string -- the caller's own text in canonical + * form, outer braces stripped and hex-float literals rewritten to + * decimal -- and never calls get_config. * get_config is used when no string was stored (for example, a filter * added through the raw cd_values API). How a filter encodes values * into cd_values is entirely private to that filter. * - * \note When reconstructing \c float or \c double values, formatting with the - * C99 \c \%a specifier (a hexadecimal float literal) round-trips through - * \c strtod with no rounding, unlike \c \%g / \c \%f / \c \%e. + * \note When reconstructing \c float or \c double values, format with + * \c \%.16e and nothing else. That is \c DBL_DECIMAL_DIG (17) + * significant digits, the minimum that round-trips every IEEE 754 + * double, and it always carries a decimal point and exponent so a TOML + * parser types the result as a float rather than an integer. + * + * Do \b not use \c \%a: a hexadecimal float literal is not valid TOML, + * and get_config output must parse for readers that are not the HDF5 + * library. (\c \%a is still accepted on \e input to set_config, which + * rewrites it to decimal.) Do not use \c \%g, which drops the decimal + * point for whole values so "8.0" becomes "8" and reads back as an + * integer; nor \c \%f; nor fewer than 17 significant digits; nor more + * than \c DECIMAL_DIG of them, since C11 7.22.1.3p11 recommends correct + * rounding only within that bound. + * + * \note Recovering a value from a decimal literal requires the reader's + * decimal-to-binary conversion to be correctly rounded. C11 7.22.1.3p11 + * only recommends this, in contrast to p9, which requires it for the + * hexadecimal form. A 1 ulp difference is enough to change behaviour in + * a filter that quantises on a binary boundary. * * \since 3.0.0 */