D533 - libm splits in two, and only half of it belongs in a differential

measured - 2026-09-04 (twenty-eight cases against glibc 2.39)

differential   345  ->  373 cases

The census was incomplete, and the filter was mine

D532 asked which implemented libc functions have no differential case, and filtered the answer to string- and number-shaped names. That filter hid around fifty math functions, every one implemented and none ever compared - a negative from a filter being a fact about the filter (check 4), applied to my own census a tick after writing it up.

The full list is 437 uncovered. Most is correctly out of scope - pthreads, sockets, files, the C++ ABI - because none of it is a pure function comparable by value. libm is the part that is.

And libm splits in two

The split is the finding:

That second half is the same trap as rand (glibc's generator) and strerror (glibc's wording)

Bit patterns, because a decimal hides the case that matters

The reference records the input and the answer as raw bit patterns, which is what strtod already does here. A decimal rendering hides last-place differences - and it hides the sign of zero.

That is not a detail. round(-0.5) is -0.0 by the round-half-away-from-zero rule, and an implementation answering +0.0 passes every comparison that goes through a string.

The break, and the case that stayed quiet

round is half-away-from-zero. The hardware's default is half-to-even, so a shim built on a rint-style instruction is wrong on exactly the halfway values. Breaking it that way:

round/half-up:                returned 0x0,                 glibc 0x3ff0000000000000
round/half-away-from-zero:    returned 0x8000000000000000,  glibc 0xbff0000000000000
round/two-and-a-half:         returned 0x4000000000000000,  glibc 0x4008000000000000
round/minus-two-and-a-half:   returned 0xc000000000000000,  glibc 0xc008000000000000

Four cases, and the second one is -0.0 against -1.0 - the sign-of-zero point, appearing on its own.

round/one-and-a-half did not fire, because half-to-even also answers 2 for 1.5. That is the part worth keeping: the halfway cases discriminate rather than failing as a block, which is what says they are testing the rule and not merely the neighbourhood.

What this cannot prove

That the console's libm agrees with glibc on these six. It cannot - the claim is that both implement the specified answer, and for these there is one. Where the specification permits a range, no differential can settle it, and this does not pretend to.