underflow and floating-point pitfalls
Floating point can represent an enormous range, but the range is not infinite at either end. Overflow is the familiar problem: a result too large to represent becomes infinity. Underflow is its mirror image at the small end: a result so close to zero that it falls below the smallest exponent the format allows, and the number collapses toward zero, losing its remaining digits. Picture a ruler whose finest tick marks get no smaller — a length tinier than the smallest tick simply cannot be drawn, so it reads as nothing.
IEEE-754 softens the underflow cliff with subnormal (denormal) numbers. Normally every value has a hidden leading 1, but for magnitudes below the smallest normal number the format drops that requirement and lets the leading bits be zero, so it can still represent values gradually smaller than the smallest normal — gradual underflow, fading toward zero instead of snapping there. The price is precision: subnormals carry fewer significant bits the smaller they get, and on some hardware they run much slower, which is why performance code sometimes enables a flush-to-zero mode that treats subnormals as exactly zero (faster, but less accurate).
Underflow is just one entry in floating point's catalog of honest surprises, and a beginner should expect them rather than be alarmed. Addition and multiplication are not associative: (a + b) + c can differ from a + (b + c), so the order of summation changes the answer and parallel reductions may not match serial ones. Numbers you expect to be equal may not be (0.1 + 0.2 is not exactly 0.3 in binary), so you compare with a tolerance, not with exact equality. Subtracting nearly equal numbers destroys precision (catastrophic cancellation). And the standard defines special values — positive and negative zero, infinities, and NaN (not a number) for things like 0/0, where NaN is unequal to everything including itself. None of these are bugs; they are the documented, reproducible behavior of a system that trades exactness for range and speed.
In single precision, summing 0.1 + 0.2 yields 0.30000001192..., not exactly 0.3, so the test (0.1 + 0.2) == 0.3 is false. And summing a huge number with many tiny ones in different orders gives different totals — addition is not associative.
Compare floats with a tolerance, not exact equality; sum order changes the result.
These are not hardware bugs but documented IEEE-754 behavior. The practical rules: never test floats for exact equality, beware the order of long sums, and avoid subtracting nearly equal quantities. Flush-to-zero trades subnormal accuracy for speed — know which you have enabled.