od: float output for -t f2/f4/f8 does not match GNU (trailing zeros, e-notation threshold, NaN spelling)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 24.1k
- Forks
- 2k
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 365
Description
Summary
od's floating-point output (-t f2, -t f4, -t f8, and the fH/fB aliases) does not match GNU. GNU prints the shortest decimal representation that round-trips, rendered with printf's %g rules; uutils instead prints a fixed number of significant digits and switches to scientific notation far too early.
The most visible consequence is that ordinary values print incorrectly — od -t f4 of 1.0 gives 1.0000000 instead of 1, and 0.01 gives 9.9999998e-3 instead of 0.01.
Steps to reproduce
$ printf '\x00\x00\x80\x3f\x0a\xd7\x23\x3c\xac\xc5\x27\x37\x00\x00\x20\x41' | od -An -t f4
(the four little-endian float values 1.0, 0.01, 1e-05, 10.0)
GNU coreutils 9.7:
1 0.01 1e-05 10
uutils (this repo, a01c36d):
1.0000000 9.9999998e-3 9.9999997e-6 10.000000
Same for double, with 1.0 and 0.1:
$ printf '\x00\x00\x00\x00\x00\x00\xf0\x3f\x9a\x99\x99\x99\x99\x99\xb9\x3f' | od -An -t f8
GNU : 1 0.1
uutils : 1.0000000000000000 0.10000000000000001
Distinct defects
Comparing every one of the 65536 half-precision bit patterns (-t f2) against GNU, 10624 render differently. They fall into four groups:
- Trailing zeros are not stripped.
1→1.0000000,0.25→0.25000000. - Scientific notation kicks in far too early. uutils switches at an exponent of
-2; GNU (following%g) switches only below1e-5. So0.01→9.9999998e-3instead of0.01, and0.0625→6.2500000e-2instead of0.0625. - Exponents are not zero-padded to two digits.
5.9604645e-8instead of GNU's5.9604645e-08. NaNis spelled with the wrong case and loses its sign. GNU printsnan/-nan; uutils printsNaNfor both.
Because uutils pads to a fixed significant-digit count instead of using the shortest round-tripping form, some values also gain visible representation error that GNU never shows — 1e-05 prints as 9.9999997e-6, and 1e38 prints as 9.9999997e+37 instead of 1e+38.
The half-precision path is inconsistent with the wider types today: format_item_f16/format_item_bf16 in src/uu/od/src/prn_float.rs run their result through trim_float_repr, which fixes defect (1) for those types only, while format_item_f32/format_item_f64 do not.
The rule GNU follows
I derived this purely by black-box comparison against the gnu* binaries (no GNU sources were consulted). Let
D= the smallest number of significant digits whose decimal rendering parses back to exactly the same value (≤ 9 forfloat, ≤ 17 fordouble),X= the decimal exponent,P = max(D, DIG)whereDIGis 6 forfloatand 15 fordouble(FLT_DIG/DBL_DIG).
Then GNU renders scientific notation with D-1 fractional digits when X < -4 || X >= P, and fixed notation with D-1-X fractional digits otherwise, stripping trailing zeros in both cases — i.e. exactly %g with the shortest round-tripping precision, except that the fixed-vs-scientific choice uses at least DIG digits.
I validated this model against GNU on all 63490 non-NaN half-precision values and on 2462 float / 2468 double samples (all powers of ten from 1e-45 to 1e38, random bit patterns including subnormals, and random decimals): it reproduces GNU's output exactly, with zero mismatches.
Note this is not a recent GNU change — od from GNU coreutils 8.30, 8.32, 9.4 and 9.7 all agree on the outputs above.
Environment
- uutils commit
a01c36d14aa9101a0254eeafe28fe30fde92b7f9(od (uutils coreutils) 0.9.0), release build - GNU coreutils 9.7 (also checked 8.30 / 8.32 / 9.4)
- Ubuntu 25.10, x86_64,
LANG=C
Out of scope
-t fL (long double) is broken for an unrelated reason — the 80-bit x87 value is read as an f64, so od -An -t fL prints 0 where GNU prints 1. That's a decoding bug rather than a formatting one and isn't covered here.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/uu/od/src/prn_float.rs, especially format_item_f16, format_item_bf16, format_item_f32, format_item_f64, and trim_float_repr. Reproduce the examples with od -An -t f4 and -t f8, then make floating-point output follow the described shortest round-tripping and %g-style rules, including exponent padding and signed NaN spelling. Done means the listed values and the half-precision comparison match GNU.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100