# What is the rationale behind implicitly widening integer types in numeric operations?

Languages such as Java and C specify implicit widening of integer types for numeric operators, especially arithmetic operators, to a minimum of 32 bits. What is the rationale behind doing this?

My impression is that it's based on the end-programmer's expectations - that is, 200 + 100 should yield 300, and not overflow to 44. Is there additional impetus behind this implicit type conversion?

This decision to widen only to 32 bits seems a bit arbitrary to me. Besides performance/memory concerns (some target instruction sets don't support 64-bit numbers, so more instructions are necessary), why not widen to 64 bits to minimize overflow as much as possible? If a language were designed which required 64-bit integer promotion for the operands of a numeric operation, would a sufficiently sophisticated optimizer be able to optimize down to 32-bits where sufficient (or, given that I have a pretty poor grasp on computability, is such an optimization impossible)?

TLDR: why are 200 and 100 in 200 + 100 widened to 32 bits? Why not 64 bits? Would a language be significantly inefficient if converted operands to 64 bits, especially if implemented with a "good" optimizer?

• The biggest unexpected result I can think of in terms of semantics would be the impact on arithmetic right-shifting a constant, such as 0xff000000 >> 4 becoming 0xff00000 instead of 0xfff00000. Jul 5, 2015 at 1:50

First of all, when you write 200 + 100 the compiler deduces the type for both numbers. It's assigning int which has a width of 32bits on most languages and target architectures, so your 200 was never just 0xC8, it always were 0x000000C8 (on little endian).

Why are we still using 32bits? My guess is that 32bits is still the sweet spot for avoiding overflows while not using too much memory.

Moving from 32bits to 64bits requires twice the memory, that is trivial, but has some details.

• Twice the memory will have to be moved, and the memory copying speed won't increase.
• Twice the memory will not only reside on your ram, but also on the cache of your CPU, and while your system may have a lot of ram it still has quite limited cache. Take for example the i7-4790K, quite more powerful and expensive than your average CPU. It says it has 8MB of cache, but that is Level-3 cache, it has 4x32kB data Level-1 cache to use across it's 4 cores, now memory seems expensive.
• When I mentioned the Level-1 cache I was only bringing you closer to the real thing. Your CPU operates on it's registers and they are even more limited.

This may seem interesting now: Memory hierarchy

"Most modern CPUs are so fast that for most program workloads, the bottleneck is the locality of reference of memory accesses and the efficiency of the caching and memory transfer between different levels of the hierarchy"

Edit: I forgot to mention that because of the no-overhead backwards compatibility of the amd64 (x86-64) architecture, 32bits operations are done just as fast as 64bits operations, but probably that also applies to 16bits.

• I'm familiar with the memory hierarchy. Can you think of anything beyond performance that would drive this decision? Jul 5, 2015 at 4:54
• @skeggse: No, I would guess that these kind of decisions are mostly driven by performance. Anyways, on C/C++ you can use specific widths (<stdint.h>) so your code behaves the same on every target. Java widths are probably standard for 32bit and 64bit jvm and maybe even the same for both. Jul 5, 2015 at 5:04
• I'm asking from the perspective of someone designing a language, and wanting to understand what seems to be an arbitrary decision (beyond performance) present in a few of the languages I'm using for inspiration. In my question, I asked whether optimization would solve the problem, and based on your answer (assuming a sufficiently powerful optimizer), it sounds like using 64-bit values for intermediate computations would work. Jul 5, 2015 at 5:07
• @skeggse "what seems to be an arbitrary decision (beyond performance)" That seems like saying that fitting really powerful engines to racing cars seems to be an arbitrary decision (beyond performance). If performance is the actual reason, the decision will, by construction, appear arbitrary beyond that reason. Jul 5, 2015 at 5:23
• Even better, the 64bit operations on amd64 are on average slower. They cost more code space (=L1I misses and lower average decode bandwidth) and 64bit division is slower by a lot. 16bit operations are also slower but for different reasons (length-changing prefixes have penalties sometimes, also the huge mess of spurious dependencies on the upper halves of registers), but 16bit math does not have to be implemented with 16bit operations. Jul 5, 2015 at 10:40