Simplify with the greatest common divisor
g = gcd(W, H) on integers only. Ratio = (W/g):(H/g). Never compare floats with ===. Snap with round(x×1e9)/1e9 before you compare two decimals.

Resize formula
H′ = W × (rh/rw). W′ = H × (rw/rh). Lock the ratio, type the side you have, and the other side follows.
Convert formulas
If W/H > rw/rh (source wider): cropW = floor(H×rw/rh), cropH = H; canvasW = W, canvasH = ceil(W×rh/rw). Otherwise cropW = W, cropH = floor(W×rh/rw); canvasH = H, canvasW = ceil(H×rw/rh).
Nearest standard
Relative error |dec − dec_std| / dec_std. Keep the smallest error that is ≤ the row tolerance (default 1%, 21:9 uses 3% because marketing panels sit farther away).
Decimals to fractions
1.85 → 37:20, 2.5 → 5:2, 2.39 → 239:100. Implement by placing the decimal over a power of ten, then GCD.
Why the formulas are written this way
Floor on crop is a safety rule: a crop wider than the source is not a crop. Ceil on pad is the matching safety rule: a canvas shorter than the source would clip. Half-up is the school rounding people expect; even exists because codecs reject odd sides.
Float compare uses a 1e9 snap so 0.1 + 0.2 style noise cannot flip a nearest-standard badge. That is documented here so a future editor does not “simplify” it to ===.
The aspect ratio formula you actually type is the short one: divide both sides by their greatest common divisor, or set the missing side with H′ = W × rh/rw. Floor and ceil only enter when you are cutting or padding to a different shape. Use the short form for a resize that must keep the picture.
Frequently asked questions
What is the aspect ratio formula?
Divide both sides by the GCD, or use H′ = W × rh/rw to resize.
Why floor on crop?
So the crop never exceeds the source.
Why ceil on pad?
So the canvas always covers the source.
Why even rounding?
Many codecs want even widths and heights.
Where are the tests?
tests/fixtures.json, run by tests/test-calc.mjs.