Luhn Algorithm Validator
Validate and generate Luhn check digits (mod 10 algorithm) for credit cards, IMEI, ICCID, and other identifier numbers. Everything runs in your browser — numbers are never transmitted.
Card / Identifier Number
Result
How the Luhn Algorithm Works
- Starting from the rightmost digit (the check digit), move left.
- Double the value of every second digit. If doubling results in a number greater than 9, sum the digits (e.g., 7×2=14 → 1+4=5).
- Sum all the digits (both doubled and untouched).
- If the total mod 10 = 0, the number is valid.
Where Luhn Is Used
| Identifier | Typical Length | Check Digit |
|---|---|---|
| Credit / Debit Card (PAN) | 13–19 digits | Last digit |
| IMEI (mobile device ID) | 15 digits | Last digit |
| ICCID (SIM card serial) | 19–20 digits | Last digit |
| NPI (US healthcare provider) | 10 digits | Last digit |
| Canadian SIN | 9 digits | Last digit |
Related Tools
CRC Calculator | Crypto Checksum Verifier | EMV TLV Parser | Luhn Algorithm Guide | Credit Card Validation Guide | Luhn Tool Comparison
Real-World Luhn Applications
The Luhn algorithm is used in many identification number systems worldwide. Understanding where and how it is applied helps in developing payment systems, SIM card management tools, and device identity verification.
Credit Card Structure
Credit card numbers (PAN) follow ISO/IEC 7816 and are typically 16 digits. The structure: 6-digit BIN/IIN (Bank Identification Number) identifying the issuer, variable-length account number, and a single Luhn check digit at the end. The BIN range determines the card brand: 4=Visa, 51-55 and 2221-2720=Mastercard, 34/37=Amex, 6011/65=Discover, 62=UnionPay. This tool automatically detects the brand from the BIN and validates the Luhn check digit.
IMEI and ICCID
IMEI (International Mobile Equipment Identity) is a 15-digit number identifying a mobile device. The first 8 digits are the TAC (Type Allocation Code) identifying the model, followed by a 6-digit serial number and a Luhn check digit. The first 2 digits of TAC typically indicate the manufacturer. ICCID (Integrated Circuit Card Identifier) is a 19-20 digit number identifying a SIM card, starting with 89 (telecommunications industry identifier), followed by country code and issuer ID. Both use Luhn for the check digit. For TLV-encoded card data, use our BER-TLV Parser.
What Luhn Can and Cannot Detect
Luhn is a single-digit error detector — nothing more. Understanding its coverage prevents a dangerous over-reliance on a passing check.
| Error Type | Example | Detected? |
|---|---|---|
| Single-digit typo | 4532…0366 → 4532…0866 | Yes, always |
| Adjacent transposition | …1283 → …1823 | Yes, almost always |
| Twin transposition (non-adjacent) | 4…9…4 → 9…4…4 | Mostly — some pairs slip through |
| Double-digit typo | two independent wrong digits | ~50% slip through |
| Completely random number | 10-digit guess | ~10% pass by luck |
The last row is the critical one for testers: a random 16-digit number has a roughly 1-in-10 chance of being Luhn-valid. That's why Luhn validation is a sanity filter, not fraud detection — payment systems pair it with BIN range checks, length rules, and issuer verification. Generating test data? A Luhn-valid number is trivially constructible (our Generate tab does it), which is exactly why the algorithm protects against typos and transcription errors, and exactly why it provides zero security against fabrication.
Luhn Variants You May Encounter
Several industries extended or adapted the mod-10 scheme, and knowing the variant prevents false "invalid" verdicts:
- Verhoeff algorithm — uses dihedral group arithmetic instead of mod-10 doubling; catches all single-digit errors and all adjacent transpositions. Used where typo-proofing matters more than legacy compatibility.
- Damm algorithm — another fully error-detecting alternative with a single quasigroup table; popular in European utility meter IDs.
- Mod 11 check (ISBN-10, bank routing) — weights digits and takes remainder mod 11, using X=10; stronger detection but produces a non-digit check character.
- Luhn mod N — generalization for non-numeric alphabets; used in some airline ticket and asset tag schemes where letters are embedded in the identifier.
When a known-good number fails plain Luhn, check whether the issuer uses one of these variants before declaring the data corrupt — GSM SIMs, for example, sometimes carry provider-specific check conventions in the issuer extension digits.
Luhn Test Vectors Worth Bookmarking
A handful of canonical test values make debugging any Luhn implementation painless — keep these in your unit tests:
| Number | Valid? | What it proves |
|---|---|---|
| 79927398713 | Yes | The classic worked example from the algorithm's literature; sum is exactly 70 |
| 4532015112830366 | Yes | Visa-shaped 16-digit; if this fails, your right-to-left doubling is broken |
| 4532015112830367 | No | Single-digit mutation of the above; must be rejected |
| 5500005555555559 | Yes | Mastercard-shaped edge case where a doubled 9 yields 18 → 9 |
| 0 | No | Guards against empty/single-digit inputs sneaking through as "valid" |
If your implementation disagrees with any row above, the most common culprit is doubling in the wrong direction — Luhn always doubles every second digit starting from the rightmost, which means the check digit itself (position 1) is never doubled.
Implementing Luhn in Your Own Code
The algorithm is small enough to inline anywhere. Reference implementations, right-to-left with the doubling pattern described above:
// JavaScript
function luhnValid(num) {
const d = num.replace(/\D/g, '');
let sum = 0, alt = false;
for (let i = d.length - 1; i >= 0; i--) {
let n = +d[i];
if (alt) { n *= 2; if (n > 9) n -= 9; }
sum += n; alt = !alt;
}
return d.length >= 2 && sum % 10 === 0;
}
# Python
def luhn_valid(num: str) -> bool:
d = [int(c) for c in num if c.isdigit()]
if len(d) < 2: return False
total = 0
for i, n in enumerate(reversed(d)):
if i % 2 == 1:
n *= 2
if n > 9: n -= 9
total += n
return total % 10 == 0
Common implementation bugs: doubling the wrong parity of digits (start doubling from the second-from-right, not the leftmost), forgetting the n > 9 → subtract 9 fold, and failing to strip spaces/dashes before parsing. Test against the standard vector 4532015112830366 (valid) and 4532015112830367 (invalid) before shipping.