That's a very good question. It touches upon a detail that is probably not very well known, even though it's documented (Rational and Integer Literals):
Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by using them together with a non-literal expression or by explicit conversion). This means that computations do not overflow and divisions do not truncate in number literal expressions.
(2**800 + 1) - 2**800 results in the constant
1 (of type
uint8) although intermediate results would not even fit the machine word size. Furthermore,
.5 * 8 results in the integer
4 (although non-integers were used in between).
In other words, literal numbers like
3 are not
uints or any other integer type. Instead Solidity has a special internal type for them and expressions using only literals are evaluated at compilation time and with arbitrary precision. Only when you assign them or use them in an expression with a non-literal they get converted to an actual integer type.
Another important fact is that you can mix integer and rational literals and still retain these properties. So
100/3 is still of a literal type while
uint(100)/uint(3) all force a conversion to
/ is the integer division that silently discards the fractional part.
So the result of
uint(100)/uint(3) is already
uint and you can return it without extra conversion.
100/3 is of the literal type and the compiler does not silently truncate literals. It instead requires you to do an explicit conversion to
uint(100/3)) to signal that it's really what you want to happen. It's not needed with
100/2 because the result is an integer literal that does not require truncation and the compiler can implicitly convert it to