← board

repr of a double near the smallest normal takes minutes on soft-float targets

print("extremes:", 5e-324, 2.2250738585072014e-308)

On a C3 under QEMU this line ran until the watchdog fired. It looked like 5e-324 was the culprit, but the stall is the NEXT value, because print emits nothing until the whole line is formatted. On hosted riscv32 print(5e-324) alone takes 0.2 s, and print(2.2250738585072014e-308) alone takes 20-47 s (the range is machine load). On x86-64 it takes 0.2 s.

Cause

PyFloatRepr finds the shortest round-tripping string by rounding to 1..17 digits and parsing each candidate back with PyStrToFloatDef. The parser is exact. It runs an ordered search over bit patterns, and each step is a ~750-digit exact expansion down here. It is seeded from a float estimate (sig / 10^k), and that estimate is the only float arithmetic in the path.

A candidate such as 2.225073858507201e-308 is just below the smallest normal, so the estimate is a denormal. Soft-float riscv32 and Xtensa flush denormals to zero (bug-a-riscv32-softfloat-has-no-subnormals.md), so the seed was bit pattern 0. Nothing was wrong as a result, because the seed is never trusted. But the search then doubled its way up from 0 and bisected the whole range, once per candidate, and the value needs all 17 candidates.

So it was slowness, not a hang: no loop fails to terminate.

The fix (compiler/builtin/pylib.pas)

Measured (2026-09-29)

hosted riscv32 before after
print(5e-324) 0.22 s 0.32 s
print(2.2250738585072014e-308) 20.2 s (47 s under load) 0.33 s
the 24 values of the new test 244 s 6.6 s

Not in this fix

The same 9762-value differential found 46 values where Nil Python's repr is one digit longer than CPython's (7.1202363472230444e-307 for 7.120236347223045e-307). All 46 are powers of two above the denormals, and the previous compiler prints the same. That is a separate defect with its own commit.