Core: fix pldexp intermediate overflow for |e| >= 2^ExponentBits
What
pldexp(0, 256) returns NaN instead of 0 in single precision, and denormals scale to inf instead of their exact finite result, for exponent magnitudes >= 2^ExponentBits (256 for float/bfloat16, 2048 for double). Regression from !2527 (merged).
Why
The depth-3 multiply tree introduced in !2527 (merged) forms c1*c1 = 2^(2b) as a standalone factor of the 4-way scale split. That factor is independent of a, so for e >= 2^ExponentBits it overflows to inf on its own even when the exact result is finite or zero:
pldexp(0.f, 256)→0 * inf = NaN(should be0)pldexp(2^-149f, 256)→2^-85 * inf = inf(should be2^107)
The pre-!2527 multiply order (((a*c1)*c1)*c1)*c2 is safe because every partial product contains a: the intermediates grow (or shrink) monotonically towards the final result and cannot spuriously over- or underflow.
The packetmath test missed this because its hardest case (-min_exponent + max_exponent) probes e = 2045 for double and e = 253 for float — just below the broken region at 2048/256.
How
- Restore the sequential multiply order in
pldexp_genericand in the SSE/AVX/AVX512doublespecializations. The other !2527 (merged) improvements (single-instructionvpmovsxdqfactor construction,pnegateremoval,b_remaindercomputation) are kept; the cost of the fix is one extra dependent multiply in the full-rangepldexponly —pldexp_fast, whichpexp/pexp2use on their hot path, is untouched. - Document in
pldexp_genericwhy the multiplies must not be re-associated. - Extend the packetmath
pldexpcoverage to exponent magnitudes>= 2 * max_exponentwith zero, denormal, and unit lanes (denormal inputs are replaced bymin()on 32-bit ARM, matching the existing FTZ workarounds).
Found while writing tests for a follow-up MR that exposes pldexp as ArrayBase::ldexp() (as promised in the !2694 (merged) review discussion); that MR will be rebased on this fix.
Not released: the regression is on master only, the 5.0 branch predates !2527 (merged).