Random 16 digit number generator

Uniform sixteen-digit draws over all 10 quadrillion values, card-shaped by length and deliberately not valid card numbers.

G

This does not generate valid card numbers

Sixteen digits is the length of most payment card numbers, so it is the first thing people expect this page to produce. It does not, and the reason is arithmetic rather than policy.

A payment card number carries a Luhn check digit, the mod-10 checksum standardised in ISO/IEC 7812-1. The last digit is fully determined by the preceding fifteen: double every second digit from the right, subtract nine from any result above nine, sum everything, and a valid number leaves a total divisible by ten. Exactly one of the ten possible last digits satisfies that, so a uniformly random sixteen-digit string passes Luhn about 10% of the time and fails the other 90%.

Passing Luhn would not make it a card number either. The leading digits are the issuer identification number, assigned to a specific bank, and behind that sits an account that either exists or does not. Luhn is a typo detector, not an authorisation. A random string that happens to satisfy it is still not a payment instrument, and no processor will treat it as one.

This tool draws sixteen independent digits and computes no checksum. Nine of every ten values it produces will be rejected by any system that validates Luhn.

Use the published test numbers instead

For testing a payment integration, the correct input is the test card numbers the processor publishes. Every major processor maintains a list in its testing documentation. Those numbers are Luhn-valid, sit in ranges reserved for testing, are accepted by the processor sandbox, and are rejected outright by live systems. Stripe test Visa number 4242 4242 4242 4242 is the best known of them.

They also do something a random generator cannot: each published number triggers a specific outcome. There are numbers that decline, numbers that fail with insufficient funds, numbers that require a 3-D Secure challenge and numbers that time out. Testing against those is how you find out what your error handling does, which is the entire reason for testing a payment flow.

Use this page for card-shaped values that never touch a payment path: layout and truncation testing, fixtures for a system that stores a reference but does not validate it, or masked display work where only the last four digits are shown.

Sixteen digits break JavaScript numbers

A JavaScript number is a double, and the largest integer it represents exactly is 9,007,199,254,740,991, which is 2^53 - 1. Sixteen-digit values run up to 9,999,999,999,999,999, so about 11% of the sixteen-digit range cannot be held exactly. Parse one of those and the low-order digits change.

This is not a subtle bug. JSON.parse on a sixteen-digit unquoted number silently rounds it, so a value can survive a round trip through your API and come back different in its last digit or two. The same applies in any language whose default JSON number type is a double, which is most of them.

The defence is the same one that applies at every length on this site, just with sharper consequences here: a sixteen-digit identifier is a string. Quote it in JSON, store it as CHAR(16), and never let it become a numeric type anywhere in the pipeline. This generator draws in chunks small enough to stay exact in integer arithmetic, pads each chunk back to its full width and concatenates the text, so the value is never held as a float and what you copy is exactly what was drawn.

Where 16 digits sit against common numeric limits
LimitValueHolds all 16-digit values
Signed 32-bit integer2,147,483,647No
JavaScript exact integer, 2^53 - 19,007,199,254,740,991No, about 11% exceed it
Signed 64-bit integer9,223,372,036,854,775,807Yes
CHAR(16)Any 16 charactersYes, and keeps leading zeros

Entropy and collisions at sixteen digits

Sixteen digits give 10,000,000,000,000,000 values and 16 x log2(10) = 53.15 bits. Duplicates reach 1% at about 14.2 million draws and 50% at about 117.7 million, which is finally enough headroom that a mid-sized system can issue identifiers without a uniqueness check surviving on luck alone. A unique index is still cheaper than the incident.

As a secret, 53.15 bits is still under the 64-bit floor NIST sets for a value that stands without rate limiting. If you want a token rather than an identifier, sixteen digits is the wrong shape: sixteen hexadecimal characters give 64 bits and sixteen alphanumeric characters give 95.27, from a value of the same length. Digits are the right alphabet only when the value must be entered on a numeric keypad.

Doing this in your own code

JavaScript
// 16 independent digits, uniform, leading zeros preserved.
// 250 is Math.floor(256 / 10) * 10. Bytes at or above it are thrown away,
// which is what keeps every digit at exactly 1 in 10.
function randomDigits(n) {
  const buf = new Uint8Array(1);
  let out = "";
  while (out.length < n) {
    crypto.getRandomValues(buf);
    if (buf[0] >= 250) continue;
    out += buf[0] % 10;
  }
  return out;
}

randomDigits(16);   // a 16-character string, never a number

The usual shortcut, Math.floor(Math.random() * 9e5) + 1e5, cannot produce a leading zero and draws from a PRNG whose state is recoverable from its output.

Python 3.6+
import secrets

# Draw the digits. This never becomes an int, so leading zeros survive.
"".join(secrets.choice("0123456789") for _ in range(16))

# Or draw one integer and pad it back to width.
f"{secrets.randbelow(10 ** 16):016d}"

random.randint() uses the Mersenne Twister and is not a security boundary. secrets draws from os.urandom.

Go
import (
    "crypto/rand"
    "fmt"
    "math/big"
)

limit := new(big.Int).Exp(big.NewInt(10), big.NewInt(16), nil)
v, err := rand.Int(rand.Reader, limit)
if err != nil {
    panic(err)
}

// The width verb is the part that keeps the leading zeros.
fmt.Printf("%016d\n", v)
Java
import java.security.SecureRandom;

SecureRandom rnd = new SecureRandom();
StringBuilder sb = new StringBuilder(16);
for (int i = 0; i < 16; i++) {
    sb.append(rnd.nextInt(10));
}
String value = sb.toString();

nextInt(bound) already rejects the unusable top of the range, so it is unbiased for a bound of 10.

PHP 7+
// random_int() is backed by the system CSPRNG.
// rand() and mt_rand() are not, and must not be used here.
$value = str_pad((string) random_int(0, 10 ** 16 - 1), 16, '0', STR_PAD_LEFT);
Shell
# 16 digits straight from the kernel CSPRNG.
# tr keeps only ASCII digits, so every byte that survives is uniform over 0-9.
LC_ALL=C tr -dc '0-9' < /dev/urandom | head -c 16; echo
JavaScript (Luhn check)
// Luhn, the mod-10 checksum in ISO/IEC 7812-1. Only one of the ten
// possible last digits balances the sum, so a uniformly random
// 16-digit string passes about one time in ten.
function luhn(s) {
  let sum = 0;
  for (let i = 0; i < s.length; i++) {
    let d = s.charCodeAt(s.length - 1 - i) - 48;
    if (i % 2 === 1) {
      d *= 2;
      if (d > 9) d -= 9;
    }
    sum += d;
  }
  return sum % 10 === 0;
}

luhn(randomDigits(16));   // true roughly 10% of the time

Passing Luhn does not make a number a card. It only means the last digit is arithmetically consistent with the first fifteen.

Questions

Are these valid credit card numbers?

No. A card number carries a Luhn check digit determined by the preceding fifteen digits, so a uniformly random sixteen-digit string satisfies it only about 10% of the time. Even the ones that pass are not cards: the leading digits identify a specific issuer and the account behind them does not exist.

What should I use to test a payment integration?

The test card numbers your processor publishes in its testing documentation. They are Luhn-valid, reserved for testing, accepted by the sandbox and rejected by live systems, and each one triggers a specific outcome such as a decline or a 3-D Secure challenge. Random digits can only ever test the happy path, and not even that reliably.

Why does my code lose the last digits of a 16-digit number?

Because it became a double somewhere. JavaScript represents integers exactly only up to 9,007,199,254,740,991, and roughly 11% of sixteen-digit values are above that. JSON.parse on an unquoted sixteen-digit number rounds it silently. Keep the value quoted and typed as a string end to end.

Can a 16-digit value start with 0?

Here, yes, at the correct rate of one in ten, because each digit is drawn independently. Real card numbers do not, since the leading digits are an assigned issuer identification number. That is another reason the output is card-shaped rather than card-like.

Is 16 digits enough for a secure token?

No. 53.15 bits is below the 64-bit level NIST treats as the floor for a secret that stands without rate limiting. At the same length, hexadecimal gives 64 bits and an alphanumeric alphabet gives 95.27. Use digits only when the value has to be typed on a numeric keypad.

How many 16-digit values before a duplicate?

About 14.2 million draws for a 1% chance and about 117.7 million for 50%. That is real headroom compared with shorter lengths, but it is not a guarantee, and a unique index costs far less than reconciling a duplicate after the fact.

Related generators