Hmmm. This close-to-the-metal IEEE stuff make a "HERE BE DRAGONS!"
alarms go off in my head... (What's up with that correction by 1
to sys.float_info.mant_dig? Or, probably equivalently, why would
sys.float_info.min_exp (-1021) be off by 1 relative to log2 of
sys.float_info.min (-1022)?)
The sys.float_info constants come straight from the standard C
limits defined in float.h. The C standards choose to describe
floats in the form
sign * 2**exponent * significand
with 0.5 <= significand < 1.0. (Well, assuming base 2; the
actual standard is a bit more general than this; see e.g.
section 5.2.4.2.2 of C99.)
So from C's point of view, the smallest normal value
comes from the minimum exponent (-1021) together with
the minimum significand (0.5), so it's 2**-1021 * 0.5,
or 2**-1022. Similarly, the max value is (1-2**-53)*2**1024.
It's a bit unfortunate that the IEEE 754 standard prefers
a different normalization, with the exponent chosen so that
the significand is in [1.0, 2.0). So where IEEE 754-2008
gives emax as 1023, C gives DBL_MAX_EXP as 1024; both
describing exactly the same format.
I suppose that
2**(sys.float_info.min_exp - sys.float_info.mant_dig)
would also work?
Yes. This all only works for IEEE 754 binary formats,
though. Most other floating-point formats you're likely
to meet (IBM hex floats, VAX D and G, Cray floats, etc.)
don't have gradual underflow and subnormals.