Notes › Tech Notes › Why the last digit of a Chinese ID is sometimes X: weighted modulo 11

Why the last digit of a Chinese ID is sometimes X: weighted modulo 11

A form says the ID number is invalid and you are certain you typed it correctly, or you are writing validation code and want to know which layer to trust. Both cases come back to that one check digit: it is computed from the first 17 digits, and the X at the end is just the result of the same algorithm. The whole chain is three steps: weight, sum, take the remainder and look it up.

The structure of an 18-digit number

The first 6 digits are the address code, the next 8 are the date of birth, then 3 digits of sequence code (the ordering within one area on one day, with the last digit's parity indicating sex), and the final digit is the check digit. The first 17 digits carry all the identity information; the 18th carries none and does one job only: it lets the first 17 be verified arithmetically.

Three steps to the check digit

  1. Multiply the first 17 digits position by position by the weights 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 and add the 17 products.
  2. Take that sum modulo 11; the remainder falls between 0 and 10.
  3. Look the remainder up to get the check digit: 0 to 10 map to 1,0,X,9,8,7,6,5,4,3,2.

Walk a public sample number through it: the first 17 digits 11010519491231002 give a weighted sum of 167, 167 divided by 11 leaves a remainder of 2, the table gives X, and the full number is 11010519491231002X.

PositionDigitWeightProduct
1177
2199
30100
4155
5080
65420
7122
8919
94624
109327
11177
122918
1331030
14155
15080
16040
17224

Adding the 17 products in the right column: 7+9+0+5+0+20+2+9+24+27+7+18+30+5+0+0+4 = 167. The table is also the handiest thing to check against - if your own run does not reach 167, one weight was copied wrong.

The weights are not arbitrary

That string 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 comes from powers of 2 taken modulo 11: the weight of position i, counting from 1, is 2 to the power of (18-i) modulo 11. Working through it position by position matches every value: 2 to the 17th mod 11 is 7, 2 to the 16th is 9, 2 to the 15th is 10, all the way down to 2 to the 1st, which is 2. The lookup step has a derivation too: the check value for remainder r is (12 - r) modulo 11, which for r = 2 gives 10, and 10 can only be written X.

X is not a special marker for anyone

By the formula, the tier with remainder 2 yields a check value of 10. Every position in the number has to be a single character, and 10 takes two, so the Roman numeral X stands in for that slot. An X at the end therefore only means the weighted sum of the first 17 digits happens to be 2 modulo 11; it says nothing about birth date or registration area, and it certainly does not mark a special status.

What it can and cannot catch

Any power of 2 modulo 11 lands between 1 and 10 and is never 0: the remainder sequence is 2,4,8,5,10,9,7,3,6,1 and then back to 2, with a period of 10. Change one digit from d to e and the weighted sum shifts by (e-d) times that position's weight; for the change to go unnoticed the product would have to be a multiple of 11, but |e-d| is at most 9 and no weight is a multiple of 11, so it cannot be done. I enumerated every possible single-character change across the 17 positions: 9 alternative digits each, 153 cases in total, and 0 of them still end with the original check digit X. Escaping a typo is not unlikely; it is impossible.

What it cannot catch is transposition. The weight table repeats with period 10, so position 1 and position 11 share the weight 7, position 2 and position 12 share 9, and so on down to position 7 and position 17 sharing 2 - seven pairs in all. Swap digits between any of those pairs and the weighted sum does not change at all, so the check digit still matches. That is the ceiling shared by every single check digit scheme, not a bug in one implementation.

It also guarantees nothing beyond arithmetic: for a randomly assembled 18-digit string, the chance of hitting the right check digit is one in eleven. Running 200000 random 17-digit prefixes with a fixed seed gave 18063 strings ending in X, or 9.03%, against a theoretical 9.09% - a gap of 0.06 percentage points. In other words it is effective against slips of the hand and nearly useless against fabrication, and it cannot prove that the number belongs to a real person.

Turning a 15-digit number into 18 digits

Expanding the birth date from 6 digits (YYMMDD) to 8 (YYYYMMDD) means prefixing the year with a century digit and appending a check digit. Written without the century, the sample number is 110105491231002; padding 49 to 1949 makes the first 17 digits 11010519491231002, the three steps give X, and the full 18-digit number is 11010519491231002X. The reverse works too: drop the check digit and the century digit and you are back to the 15-digit form. When handling historical data this is the step most easily skipped, because a 15-digit number has no check digit to verify at all.

Layering it in production

Live validation in an input field only needs to go as far as the check digit, which stops typos. Firmer confirmation means a real-name verification channel, and the two layers differ completely in cost and in what they can conclude. In display contexts the number is a sensitive field: a list needs to be recognisable without showing the whole string, which is what Privacy Masking handles. To see the same idea applied to another number type, Bank Card Validator uses a different weighted modulo rule, with different weights but the same structure; and to check this site's weight string digit by digit, Chinese ID Number Checker lists the weighted sum, the remainder and the lookup result together.

Q: Changing one digit is caught, so why not always two

A single-character change alters the weighted sum, and the choice of weights makes it very unlikely to land on the same remainder. Change two digits at once and, if the two deltas cancel out, the sum and the remainder are unchanged and the check digit still matches. That is the ceiling shared by every single check digit scheme, not a bug in one implementation.

Q: Does it ignore the address code and birth date

The check digit only looks at the weighted sum of all 17 digits. It does not judge whether the 6-digit division code really exists, nor whether the date is valid or falls in a leap year. Full format validation usually needs an extra birth-date range check, which is separate logic.

Q: Is a lowercase x acceptable

The rule says X. Most systems tolerate lowercase on input and convert it, but storage and comparison should settle on one form, otherwise the same number ends up as two different records because of letter case.

📎 Related articles

PNG compressed three times only lost 426 bytes: the real boundary of lossless compression 2026-10-22 How Many Times Bigger a PNG Gets After Converting From WebP: When It Is Unavoidable and How to Keep the Growth Down 2026-10-22 Three Reasons the Small Text Smudges When a Screenshot Is Shrunk to a Quarter 2026-10-22 Cropped to the Same 300x300, Why Does the Avatar Always Sit Half a Cell Low-Right 2026-10-22 Forecasting 18 months at 1.7 times a month: why the script and the sheet give two numbers 2026-10-22