Don't make me tap the sign: the majority of processors running C are weird little dirtbag chips of 16 bits or less sprinkled by the dozen.
And I bet that when you have only 16 bits of address space, you care how many bits every integer has.
ILP64 (wherein int is 64 bits) exists. It's not very popular, but it exists; e.g. ICC supports it. So it happened in the past once already; it may again happen in the future. In any case, predicting the future is very hard, you really shouldn't be doing this.
> IEEE memory representation of float and double
Wait, what? I'm fairly certain that a) IEEE does not mandate the in-memory representation, and b) ARM actually uses big-endian byte order for floats/doubles when storing them in memory.
> always behave exactly the same (leaving out strange edge cases such as denormals)
So not always, but please pretend so? Yeah, no, thank you.
> platforms have very long agreed on twos-complement for negative integers. This even made it into the standard at some point, I believe.
Only in C23. It was explicitly rejected for C++ 23 (and C++ 26 too, I believe).
> but because everyone of course did the obvious
No, not everyone did the obvious. That's why it took so long to standardize because divergent implementations existed.
> There are much more guarantees modern C++ code can (and should) rely on.
As long as you only use only GCC (or Clang) exclusively, yes, you can. Otherwise, no, you can't and shan't.
ILP64 is problematic for existing code: there is lots of stuff like hashcode computations using uint32_t with multiplications, relying on the C standard guaranteeing wraparound for unsigned overflows. But with 64-bit int, uint32_t will promote to a signed int, and overflows will thus be undefined behavior. This problem already exists with uint16_t multiplications on current architectures, but moving the problem to uint32_t will cause trouble for a lot of existing code that thought using fixed-size types like uint32_t would be safe.
I chose to deal with this problem by doing a "pointless" operation to force a promotion to at least unsigned int. For example:
uint16_t x = 0xFFFF;
uint16_t y = 0xFFFF;
uint16_t z = (uint16_t)((x + 0U) * (y + 0U));
This piece of code will work on any machine, such as: (uint16_t = unsigned short = 16 bits, uint32_t = unsigned int = 32 bits); (uint16_t = unsigned short = unsigned int = 16 bits, uint32_t = unsigned long = 32 bits).Wrong. You mentally casted each operand to int16_t before subsequently casting to int32_t. The first step is unjustified.
The correct calculation according to the C standard is: (int32_t)0xFFFF * (int32_t)0xFFFF, which definitely overflows.
Yeah, except that multiplying two 32-bit values, recast as 64-bit signed integers, will not overflow. Even adding another 32-bit value to this product will not overflow. Throw in the final cast to uint32_t to throw away the upper sign bits, and you get the identical result.
Factually wrong. Consider: (int64_t)0xFFFFFFFF * (int64_t)0xFFFFFFFF. It definitely overflows.
That's not how I interpret section 3.2 in the standard[1]. Figure 1 seems quite explicit in how a single and a double should be encoded. The section on extended values specify they can be encoded in an implementation-depended manner, which makes the case stronger IMO.
edit: I note that in the 2008 revision[2], it's more explicitly mentioned that the specified encoding is a binary interchange format. So that's a lot more specific than the original.
And of course, if you accept the network byte order as the one intended for the interchange, then IEEE-754 mandates big-endian encoding.
>Only in C23. It was explicitly rejected for C++ 23 (and C++ 26 too, I believe).
It was added in C++20[0], see the note[1] "This is also known as two's complement representation".
[0] https://timsong-cpp.github.io/cppwp/n4868/basic.fundamental#...
[1] https://timsong-cpp.github.io/cppwp/n4868/basic.fundamental#...