In an attempt to see what would happen in the case of a float underflow I found that I could make float numbers much smaller than FLT_MIN. I'm using xcode 5.1 on OS 10.9. The language dialect is gnu99.
int main(int argc, const char * argv)
float underflow = FLT_MIN * 0.0000004;
printf("Float min is %f or %e.\nUnderflow is %f or %e\nMin float exp is %d.\n", FLT_MIN, FLT_MIN, underflow, underflow, FLT_MIN_10_EXP);
Float min is 0.000000 or 1.175494e-38.
Underflow is 0.000000 or 4.203895e-45
Min float exp is -37.
2 possibilities to get "below minimum":
float numbers have 2 ranges: full precision (normal range) from
FLT_MAX down to
FLT_MIN and a 2nd range with reducing precision from
FLT_MIN down to
FLT_TRUE_MIN. This 2nd range, called "subnormal" typically provides about 10^-7 more range.
FLT_TRUE_MINis the "minimum positive floating-point number"
FLT_MINis the "minimum normalized positive floating-point number"
FLT_MIN_10_EXPis the "minimum negative integer such that 10 raised to that power is in the range of normalized floating-point numbers"
0 < FLT_TRUE_MIN <= FLT_MIN <= 10^FLT_MIN_10_EXP <= 10^-37
Math performed as
printf() coverts each
float passed to it to a
double. C allows code to optimize such that the value passed to
printf() may be the
double product of
FLT_MIN * 0.0000004.
float underflow = FLT_MIN * 0.0000004; printf("%e\n", underflow);
Had the output been
4.701976e-45 rather than
4.203895e-45, this would have been the case.
Note on "subnormal". A compelling reason for subnormal (or denormal) numbers lies in the following problem.
float a,b; ... // somehow a and b are set. // Are the 2 below equivalent? if (a == b) foo(); if ((a - b) == 0) foo();
Without subnormal numbers, 2 nearly the same value numbers near
FLT_MIN would have a non-zero mathematical difference much below
FLT_MIN and the result would round to
With subnormal numbers, the difference of every pair of different
floats is representable by something other than
+0.0, -0.0. Signed zeros have their own peculiarities.
In really simple, not exact terms, floating points are stored as 0.xxxxx x 2^yyyyyy. "Normal" numbers are required to NOT have leading zeros in the xxxxx part. So the smallest number you can make is something like 0.10000 x 2^-111111. However if you "cheat" and denormalize the number you can make one like 0.000001 x 2^-111111, which is smaller but has fewer significant digits.
Representing a floating number as
y = (+/-) significand x base ^ (exponent - precision), every
y != 0 has a unique representation if you make sure that
significand >= base ^ (precision - 1). A nonzero
y that satisfies this is called normalized. Now
FLT_MIN is the minimum normalized positive
FLT_TRUE_MIN is the true minimum you get without the normalized-restriction.
In other words,
FLT_MIN = base ^ (FLT_MIN_EXP - 1) and
FLT_TRUE_MIN = base ^ (FLT_MIN_EXP - precision).