The issue is that many beginner (and some experienced) programmers treat what should be opaque strings as numbers and throw them into a 32/64-bit integer, or worse, a double precision float (in JavaScript). This can result in data corruption if not done carefully. Then, when something comes in to challenge the programmer's expectations, the program breaks. For example, ZIP/postal codes are 5 numeric digits in the US, but in CA, they're of the form "A1A 1A1" (notice the space and letters). JavaScript's `parseInt` will return NaN on a Canadian postal code. Another one is house numbers; They look numeric, but can have letters or even fractions(!) in them.
Does the number 21 "contain" the number 2? Not in a meaningful sense.
However, the string "21" definitely contains the substring "2".
The digits in a credit card number are meaningless. They have no relation to each other. If you choose two digits next to each other one doesn’t represent groups of 10x as much as the other digit. Or any other quantity.
The digits are effectively just symbols, not quantities like in a mathematical representation.