Wade not in unknown C++ waters. Shift operators .
viva64.com
viva64.com
An interesting paper on a related subject:
https://wiki.engr.illinois.edu/download/attachments/15820390...
To entice you to read it, a quote:
To find the time bombs, we altered IOC’s overflow handler to return a random value from any integer operation whose behavior is undefined by the C or C++ standard. This creates a high probability that the application will break in an observable way if its execution actually depends on the results of an undefined operation. Perhaps amusingly, when operating in this mode, IOC is still a standards-conforming C or C++ compiler—the standard places no requirements on what happens to a program following the execution of an operation with undefined behavior.
I don't know what exactly the best solution is, but I'd probably default to arbitrary-precision integers which have a highly efficient implementation for native word-sized values and a safe, non-overflowing fallback that can still be made to be pretty fast. Repeated growth checks could be eliminated in many places by some basic static analysis. If you find your code would be faster without the safety, you can always selectively switch to unsafe semantics.
Basically, I think the current situation of overflow-by-default is a premature optimisation that's causing far too many bugs.
The use of 64 bit integers may significantly reduce the possible places for wrap-around to occur, but I think we (C,C++,Go etc. developers) have to develop a sense of code smell for overflow situations, so that "innocuous-looking code" stands out. That or static analysis.
The most famous example for this kind of bug is probably the binary search implementation in Java:
http://googleresearch.blogspot.com/2006/06/extra-extra-read-...
The interesting thing about this bug and many like it, is that you don't need real arbitrary-precision arithmetic to avoid it. The result of the expression (a + b) / 2 can be proven to safely fit into 32 bits if a and b are known to fit into 32 bits as well - the compiler just needs to make the result of the intermediate expression (a + b) be stored 33 bits, or to transform the it into a mathematically equivalent expression that has the same properties as the original expression with a 33-bit intermediate.
Of course, this only can be applied to a subset of algorithms - I suspect this quickly devolves into the halting problem in general. But maybe new languages could specify that such halting problem cases need to be manually determined (explicitly choose between arbitrary precision arithmetic and truncation). The simpler cases can be deduced automatically. I guess that means in addition to uint32_t and uint64_t we'll probably need more specific types, such as "positive (nonzero) integer in the range 1 to 1 << 32". Which would be tedious if you had to specify it explicitly all the time, so you'd need good inference and solid support for generic functions.
Also interesting, light travels about 1000 ft in a microsecond.
The Intel i7 has 6 operation execution ports per core, 3 of which can be used for basic integer operations. Shifts execute in a single clock cycle, but since there are 3 ALUs it is possible to execute 3 shifts per clock cycle.
There are a class of low-latency algorithm optimizations based on making sure that all operation ports are being used nearly every clock cycle. It is a special type of fixed, low-level parallelism that most software does not do very well by default. I've seen intentional and careful ALU saturation generate 2x performance gains for algorithms. The resulting code often does not look very different since it is about slightly reorganizing highly localized dependencies. (This is how hyperthreading works; unused execution ports in a clock cycle are given to a second thread to use.)
Modern compilers will replace multiplies by constant factors of 2 with shifts, making your code cleaner and more readable.