I recently started solving some problems on LeetCode where I came across a question that asks to write a function that checks whether a non-negative integer x
is a palindrome, i.e. whether x
reads the same backwards as forwards (e.g. 121
, 1221
, 91919
).
One approach to check whether a number is a palindrome is to reverse its digits and compare whether the two numbers are the same. For that, assume that we have the following implementation of a reverse_digits
function:
int reverse_digits(int x) {
int acc = 0;
while (x != 0) {
acc = acc * 10 + x % 10;
x = x / 10; // integer division, rounds down
}
return acc;
}
However, if we use a programming language in which the int
type has a fixed width, then this function is prone to overflows. If we assume, for example, that an int
has 32 bits, then, using the above implementation, we get:
reverse_digits(1_000_000_001) -> 1_000_000_001 # correctly reversed
reverse_digits(1_000_000_009) -> 410_065_409 # incorrect
My question is:
For the given implementation of reverse_digits
and a given number of bits in the int
type, is there a non-negative integer x
that is not a palindrome but for which x == reverse_digits(x)
(due to the overflow caused by the finite number of bits)? We assume that x
does not have any leading zeros.
I manually checked the 4-bit case and I wrote a small program in C that checks unsigned 32-bit integers by brute-force; in both cases, there is no such x
. However, I am not sure how to prove that there is no such integer for the 64-bit case, or how to find one if there is.
Observation 1:
The largest unsigned 64-bit integer is ULONG_MAX := 18_446_744_073_709_551_615
(with underscores added for readability) which starts with a 1
and has 20 digits. For an overflow to happen, x
must have at least 20 digits and not end in zero. Otherwise, reverse_digits
correctly reverses the number.
For x
to have 20 digits, it must start with a 1
, as 20 digit numbers that start with a number greater than 1
don't fit into 64 bits.
Observation 2:
Let $(x_{1}x_{2} \dots x_{20})$ be the (decimal) digit representation of $x$, where $x_i$ is the digit at position $i$ of $x$. Since $x$ starts with $1$, we have $x_1 = 1$.
In the last iteration of the while-loop we calculate
$$(x_{20}x_{19} \dots x_{2})*10+x_1.$$
For an overflow to happen,
$$(x_{20}x_{19} \dots x_{2})*10+x_1 \gt \text{ULONG_MAX},$$
i.e. $(x_{20}x_{19} \dots x_{2}) \gt \text{1_844_674_407_370_955_161}$, i.e. the reverse of the last 19 digits of $x$ must be greater than that value.
Observation 3:
Let $y:=(x_{20}x_{19} \dots x_{2})$ be greater than $\text{1_844_674_407_370_955_161}$. It follows that $z:= y*10+1$ causes an overflow, which means that the binary representation of $z$ has more than 64 bits.
- Let $(a_1a_2 \dots a_{64})$ be the 64-bit binary representation of $(x_{1}x_{2} \dots x_{20})$.
- Let $(b_1b_2 \dots b_{64})$ be the 64-bit binary representation of $(x_{20}x_{19} \dots x_{2}) = y$.
- Let $(c_1c_2 \dots c_{64}c_{64+1} \dots c_{64+j})$ be the (64+j)-bit binary representation of $z$.
We know that
$$
(c_1 \dots c_{64+j}) = (b_1 \dots b_{64}) * 1010_2 + 1_2.
$$
We need find bits $a_i$ and $c_i$ such that
$$
(c_{1+j} \dots c_{64+j}) = (a_1a_2 \dots a_{64})
$$
or, alternatively, show that such bits cannot exist.
This is where I'm kind of stuck. My next step would be to figure out what values $j$ can take and then do the multiplication to try to find some relation between the $a_i$, $b_i$, and $c_i$. I'm not whether this is the right approach though.
Best Answer
If you aren't wedded to base-two computers, then there are examples. Some are very silly; e.g., suppose your computer works in base $18$, and can only handle one digit numbers. Then consider the number $13$. It's not a palindrome. Its reversal is $31$, which, in base $18$, is $1(13)$ (one eighteen, plus thirteen ones). That overflows your one-digit computer, so it drops the overflow digit, and reports the reversal of $13$ to be $13$ – voila! a palindrome.
A less silly example is $1904$ in a base-three computer with seven-digit numbers.
$(1904)_{10}=(2121112)_3$.
$(4091)_{10}=(12121112)_3$.
The reversal causes overflow. Dropping the overflow digit leaves us with $(2121112)_3=(1904)_{10}$, so $1904$ gets reported as a palindrome. If seven digits seems a little small, I'm confident that bigger examples can be constructed for base-three computers.
For base two, here's an argument that this phenomenon can't happen. Let $b$ be the number of bits our machine can handle. Let $n$, our candidate palindrome, have $b$ bits when written in base two, so $$ 2^{b-1}\le n<2^b $$ Let $n$ have $d+1$ digits when written in base ten, so $$ 10^d\le n<10^{d+1} $$ Let's write $\tilde n$ for the (base ten) reversal of $n$. Then $$ \tilde n-n<9\times10^d $$ Also, $\tilde n-n$ is a multiple of nine. It consists of the overflow bits resulting from the reversal of $n$, so it's also a multiple of $2^b$. So $$ \tilde n-n\ge9\times2^b $$ These two inequalities give $$ 9\times2^b<9\times10^d $$ so $$ n<2^b<10^d\le n $$ contradiction!