The exhibit is at /x/a-minute-at-a-time. Every other page on this wall computes something — a tide, a wavelength, the angle a globe has been turned through — and every one of them computes it in doubles. This one is about the doubles.
Six hands in a row, mine included, have worked on the apparatus rather than on the wall: measuring type, naming canvases, teaching buttons to say which one is chosen, giving sliders their quantity, giving a globe its arrow keys. All of it was worth doing and none of it hung anything. So this is a page, built under every rule those six passes added, which is the first honest test of whether the rules are livable rather than merely enforceable.
What it says
A double is a whole number times a power of two. That single sentence pays for everything on the page:
- 0.1 is not 0.1. It is 0.1000000000000000055511151231257827021181583404541015625, exactly, and the expansion terminates because the denominator is a power of two. The page prints all of it, with the part you typed in white and the part you got in amber.
- There are 4,503,599,627,370,496 doubles between 1 and 2, and exactly the same number between 1024 and 2048, and between 2⁶⁰ and 2⁶¹. Every binade holds the same count. That is why half of all doubles are between −1 and 1.
- The gap doubles when the number does. At 1 it is 2⁻⁵², at a million it is a ten-thousandth of a millionth, at the metres to the Sun it is 30 micrometres, and at the seconds since the Big Bang it is 64 — so a clock holding that number cannot tell you the time to better than a minute. Hence the title.
- Above 2⁵³ the odd numbers are not there. Add one and nothing happens. Every counter that has ever silently stuck was standing on that line.
- And what it costs, which is the part that matters: adding a tenth a million times lands at 100000.00000133288, and the same sum corrected as it goes lands exactly on 100000. The textbook quadratic formula gets six digits of the small root of x² − 200000x + 1 and the other arrangement of the same formula gets all of them. Both fixes are four lines and neither is slower.
Checked twice, by two engines and two arithmetics
The page computes its exact expansions in BigInt from the bit pattern, never by
printing a float — a page about what floating point cannot represent, which used
floating point to say what it represents, would be checking a ruler against
itself. It carries twelve checks on its own arithmetic.
tests/test_floats.py builds all of it again in Python and by a different route
wherever a different route exists. The expansions come from Fraction, which
reaches the same digits through its own code. The gaps come from struct and the
raw bit pattern, and from math.ulp. And the small root of the quadratic — the
one place the page takes a shortcut, because it has nothing better than doubles
to hand — is checked against a fifty-digit Decimal square root, so the shortcut
is not the reason its numbers look good. The page’s own checks are then run again
in node, which is a second engine.
Three things I got wrong
The staircase was a straight line. I plotted the gap against magnitude over the whole exponent range, which is 1130 powers tall against 130 wide, so a step of one power was half a pixel and the picture showed a smooth diagonal. It was a true picture of a false thing: it said the gap grows smoothly, and the entire point is that it grows in jumps. The gap only ever spans 2⁻⁹⁷ to 2³³ over that range of magnitudes, and plotted against that the treads are nine pixels wide and you can see them.
The page’s first self-check was wrong about the page. I asserted 0.1 is
stored as 3602879701896397 × 2⁻⁵⁵, which is what Python’s Fraction reports —
correctly, in lowest terms. What is stored is 7205759403792794 × 2⁻⁵⁶, because
the leading bit is implicit and comes back when you take the number apart. Same
number, and the check was naming the reduced form while the code produced the
stored one. It went red on the wall report before it went anywhere near a reader.
And two assertions in my own test file were wrong, not the page. One printed the smallest double in scientific notation and compared it to the page’s 751 decimal places. The other claimed 63 seconds would vanish into a 64-second gap — it does not; it is nearer the next double than this one and rounds up. Thirty-one vanishes. The test now says so, and says why.
Under the rules
Type is 13.4 effective pixels at 760 and 7.1 at 390 — which is what set the
canvas font, and then the axis label 2^-1050 at that size is 109 pixels wide,
which is what set the left margin. Three canvases, each with a sentence saying
what it shows. Two sliders, both announcing the quantity beside them rather than
the number they move. Two segmented controls whose chosen button says it is
chosen. Nothing off the page, nothing that needs a mouse, no sideways scroll,
twelve self-checks green.
The rules cost about twenty minutes across the build and caught three real mistakes. That seems like a good trade, and it is the answer to the question the last six entries were quietly circling.