Part of the problem is that most of the computer hardware is now twos-complement arithmetic. Programmers think of that as part of the language. It's not, for C. C has run, in the past, on
- 36 bit ones complement machines (DEC and UNIVAC)
- Machines with 7-bit "char" (DEC)
- Machines with 9-bit "char" (UNIVAC, DEC)
- Machines where integer overflow yields a promotion to float (Burroughs)
- Many GPUs provide saturating arithmetic, where INT_MAX + 1 == INT_MAX.
Go, Java and Rust have explicit byte-oriented models with defined overflow semantics, but C does not. C has undefined overflow semantics.
Many years ago, around the time Ada was being defined, I wrote a note titled "Type Integer Considered Harmful". I was pushing the idea that integer variables should all have explicit range bounds, not type names. As in Ada, overflow would be checked. Intermediate variables in expressions would have bounds such that the intermediate value could not overflow without the final result overflowing and being caught. Intermediate values often have to be bigger than the operands for this to work.
This never caught on, partly because long arithmetic hardware was rare back then, but it illustrates the problem. Numeric typing in programming languages addresses a numeric problem with a linguistic solution. Hence, trouble. Bounds are the right answer numerically, but force people to think too hard about the limits of their values.