Nib explains · Computer arithmetic

Bigints for Float People

Most computer math is floating point: fast, and a tiny bit wrong at every step. There is a second kind of machine arithmetic, exact integers of any size, where nothing is ever rounded. It's how a laptop can prove that a 62-bit prime divides a number with more than 10 quadrillion digits, and be completely sure.

Nib says: If you've ever been told "never compare floats with ==", this page is for you. Green means exact integers, rust means floating point, all the way down. Every toy runs in your browser, so poke them.

1. Where floating point runs out of integers

A double (the usual 64-bit floating-point number, and every Number in JavaScript) keeps 53 significant bits, counting a hidden leading 1. Every whole number up to 253 fits exactly. Past that, the gap between neighbouring doubles is 2, then 4, then 8, and adding 1 does nothing at all.

Add one to 2n

Drag n past 53 and watch the +1 vanish on the left.

float64 (JavaScript Number)

exact integer (JavaScript BigInt)

Those 64 bits are a sign, an 11-bit exponent and a 52-bit fraction. Type any number to see how it's really stored, and the exact value hiding behind it.

The 64 bits of a double

Try 0.1, then 0.5, then 9007199254740993.

sign (1)exponent (11)fraction (52)

2. How a bigint is stored: digits in a huge base

A bigint (arbitrary-precision integer) is an array of machine words, called limbs. It's the grade-school picture of a number as a list of digits, with the base raised from 10 to 230 (Python) or 264 (the GMP library). The array grows as the number grows, so nothing is ever rounded or dropped.

Any integer, as Python stores it

Base 230, least significant limb first. It starts with F7 = 2128 + 1. Paste in something enormous.

Nib says: Vellum would want me to add a footnote. "BIGINT" in SQL is not a bigint: it's a plain 64-bit integer that overflows like any other. C has no built-in arbitrary-size integer either (C23's _BitInt is still a fixed width), so C programmers use GMP's mpz_t. Python's int, Java's BigInteger and JavaScript's BigInt are the real thing, built into the language.

3. Karatsuba: three products instead of four

Schoolbook multiplication of two k-digit numbers takes k² digit products, so doubling the length quadruples the work. In 1960 Andrey Kolmogorov suggested at a Moscow seminar that k² couldn't be beaten. Anatoly Karatsuba, a 23-year-old student in the room, found a way to beat it within about a week.

Split each number in half around a base B (here a power of 10):

x = x₁·B + x₀    y = y₁·B + y₀

Multiplied out, x·y = x₁y₁·B² + (x₁y₀ + x₀y₁)·B + x₀y₀. That's four half-size products. The trick is that the middle term comes from one extra product:

x₁y₀ + x₀y₁ = (x₁ + x₀)(y₁ + y₀) − x₁y₁ − x₀y₀

So three half-size products are enough. Additions are cheap, and applied again and again to the halves, the Karatsuba algorithm costs about k1.585 instead of k² (the exponent is log₂ 3).

One split, then the full recursion

Change either number. The bottom line counts single-digit products for both methods.

The gap grows with size

Digit products needed for two k-digit numbers.

digits kschoolbookKaratsubasavingKaratsuba's share

Karatsuba is the first rung of a ladder. GMP sticks with schoolbook below a tuned threshold (as low as 10 limbs on some processors), where its simplicity wins, then switches to Karatsuba, then to Toom–Cook (splitting into three or more pieces), then to FFT-based methods. In 2019 David Harvey and Joris van der Hoeven reached k log k, the speed Schönhage and Strassen had conjectured was the best possible back in 1971. For the 62-bit numbers in section 6, plain schoolbook is fastest: each one is only 3 limbs in Python, and a single limb in GMP. Karatsuba starts paying off somewhere from several hundred to a few thousand bits.

4. Fixed-width integers already do modular arithmetic

C's uint64_t holds 0 to 264 − 1. When a result is too big it doesn't round. It wraps around, keeping only the low 64 bits, so every operation is exact arithmetic mod 264. That's integer overflow: a different failure from floating point, and a silent one. Code that needs a product mod a prime p below 264 keeps the full 128-bit product first (unsigned __int128 in GCC) and only then reduces mod p. Fast factor sieves on graphics cards live on exactly this kind of word arithmetic.

Multiply two 64-bit numbers

a starts as the largest prime below 264. Try your own (0 to 18446744073709551615).

5. Never build the giant number

Here's the real payoff. Take the Fermat number F55 = 2255 + 1. It has 36,028,797,018,963,969 bits, or 10,845,748,610,397,182 decimal digits. Just writing it down in binary would take about 4.5 petabytes. You never need to hold it, because with modular arithmetic you can take remainders at every step:

(x · y) mod p = ((x mod p) · (y mod p)) mod p

To raise something to the power 2m, square m times and reduce mod p after each squaring. Every value along the way stays below p, so the biggest thing ever in memory is one product of two numbers below p. For a 62-bit p that's at most 124 bits. All of it exact. If you've climbed Fermat's Ladder, this is the same trick, and F55 is on its factor table.

Repeated squaring, every step exact

Euler's 1732 factor first, then a much bigger one. A gold row means you've hit −1, and the factor is proved. Scroll the table to the bottom for F55.

stepvalue mod pbits

You can check Euler's rows by hand: 256² = 65,536 = 102·641 + 154, then 154² = 23,716 = 36·641 + 640, and 640 is −1 mod 641. So 641 divides 232 + 1.

The second table is the same recipe for F55 with p = 29 · 257 + 1 = 4,179,340,454,199,820,289: 55 squarings of 19-digit numbers, landing on −1 at the last step.

6. The whole factor proof, in your browser

Showing p divides the giant is half the job. The other half is showing p is really prime, and not just "probably prime". This button runs the full check with JavaScript BigInt. We also ran it separately in Python, so it's two independent implementations of the same exact arithmetic.

p = 29 · 257 + 1

Four checks, all exact. It takes a few milliseconds.

Press "Run the proof".

The last line is the Lucas primality test, and it's a proof, not a probability. Fully factor p − 1, then find a number a where ap−1 ≡ 1 but a(p−1)/q ≢ 1 for every prime q dividing p − 1. That means a cycles through exactly p − 1 different values mod p before repeating, so all p − 1 nonzero remainders are invertible. That only happens when p is prime. Here p − 1 = 29 · 257 has only two prime factors, 2 and 29, so there are just two things to check, and a = 3 works.

7. When real numbers can't be avoided: intervals

Some proofs need real analysis: integrals, logarithms, exponentials. There you can't stay in the integers, so careful provers carry an interval [lo, hi] that is guaranteed to contain the true value. Every operation rounds the low end down and the high end up. Rounding error can widen the interval, but it can never push the truth outside it. That's interval arithmetic, and it's how computer-assisted proofs handle real numbers honestly (Python's mpmath has it built in as mpmath.iv).

x + y, two ways

The classic: 0.1 + 0.2.

float64 answer

interval that must contain the truth

The float is one number, slightly wrong, with no hint of how wrong. The interval is a little wider and carries a guarantee. (Simplified: each decimal input is widened by one step each way, then the sum is rounded outward.)

8. Which arithmetic does a result need?

Kind of resultArithmeticWhy it's safe
A factor and its primality proofexact bigintmodular squaring; there is no rounding to go wrong
A fast sieve on a graphics cardexact 64-bit wordsit can miss a factor but can't fake one: every hit is re-checked with bigints, and a good sieve must re-find known factors first
A search or count over whole numbersexact integerscomparisons between integers, as long as nothing overflows
A proof that needs logs, integrals or limitsreal analysisinterval arithmetic with outward rounding
A quick number from a spreadsheet or plain float codefloat64fine as a cross-check, never as the proof

Nib says: The habit worth keeping: before you trust a computer's "yes", ask which arithmetic said it. Exact integers can be wrong only if the code is wrong. Floats can be wrong even when the code is right.

Words on this page

Tap any word with a dotted gold underline to see what it means. Here they all are in one place.

Three things to keep

Check yourself

In JavaScript, 2**53 + 1 === 2**53 is true. Why?

A double has 53 significant bits. Just above 253, neighbouring doubles are 2 apart, so 253 + 1 rounds back down to 253. With BigInt, 2n**53n + 1n is exact and the two are different.

Karatsuba uses three half-size products instead of four. Where does the saving come from?

The middle term x₁y₀ + x₀y₁ equals (x₁ + x₀)(y₁ + y₀) − x₁y₁ − x₀y₀, and the last two products are already needed. So one extra product plus a few cheap additions replaces two products.

A uint64_t multiplication overflowed. Is the answer rounded, like a float?

No. It wraps: you get the exact product mod 264, which is usually nowhere near the true answer. That's why careful code keeps the full 128-bit product before reducing.

Go deeper