Lion Cipher Lab

Watch NaN and float rounding slip past a limit check, and fix it with Decimal and validation

Why it matters

Almost every app that handles a number from a user does the same thing: turn the text into a float and compare it to a limit. It looks safe. It is not, and the reasons are not obscure once you have seen the bits. A float is a 64-bit pattern with a fixed number of significant bits, so it cannot hold 0.1 exactly, it cannot tell 2**53 from 2**53 + 1, and it has special values, infinity and NaN (not a number), that ordinary comparison rules do not cover. Every comparison with NaN is False. So 'if amount > limit: reject' quietly approves NaN, and a NaN that reaches a running total turns that total…

What you'll learn

Steps in this lab

  1. What you are building, and what a float actually is
  2. Set up: a terminal, Python, and a lab folder
  3. Look inside a float: decode the bits with struct
  4. Write the transfer check three ways (transfer.py)
  5. Write the seeded harness (harness.py)
  6. Measure BEFORE: the naive check approves 20 of 35 edge decisions it should refuse
  7. Half a fix: fail closed and check isfinite, still with floats (20 -> 7)
  8. The fix: strict format, Decimal, integer cents (7 -> 0)
  9. JSON: Python reads and writes NaN, and parse_constant is not enough
  10. Honest negatives: what the fix costs and what it does not cover
  11. When to use which, and how a pro does it better

Open the full interactive lab →