Skip to content

// ARITHMETIC · WHAT'S UNDERNEATH //

Mario moves in fractions of a pixel

You learned never to keep money in a float. You've watched 0.1 + 0.2 come back as 0.30000000000000004 and shrugged. The NES has no floating point at all — and the trick that gives Mario his famous momentum is the same one your accounting code uses to count cents.

Half a pixel, with no fractions

The 6502 deals in whole bytes; there is no 0.5. So you split a 16-bit number down the middle: the high byte is the pixel, the low byte is the fraction of a pixel — 256ths of one. The binary point sits, fixed, between the two bytes. Now $01.80 means one-and-a-half pixels, because $80 is 128/256 = 0.5.

The carry is the glide

Each frame you add velocity to the fractional byte. Most frames Mario doesn’t cross a whole pixel at all — the fraction just accrues. When it overflows, the carry rolls up into the pixel byte and he steps one across:

6502 · 8.8 FIXED-POINT ADD
        clc
      lda pos_lo     ; the fraction (subpixels)
      adc vel_lo
      sta pos_lo
      lda pos_hi     ; the whole pixel
      adc vel_hi     ; carry out of the fraction = one pixel moved
      sta pos_hi
A plain 16-bit add. The carry between the two bytes is the instant a pile of subpixels becomes a pixel — the glide, the skid, the famous "ice."

Why floats drift and this doesn’t

Fixed point is exact for everything it can hold — halves, quarters, 256ths, no surprises. A float trades that exactness for enormous range, and pays in tiny errors, because most decimals (0.1 among them) have no exact binary form, the way 1/3 has no exact decimal. That rounding is where 0.1 + 0.2 drifts — and why money lives in integer cents or a decimal type, never a float.

“Subpixel movement” and “store money as cents” are the same instinct: pick a small fixed unit, count it in whole numbers, and let a carry do the rounding. The NES had no other option — so it shows you the mechanism with nothing on top of it.

>Every great programmer started with one instruction.