You don't have to reorder to make bugs. Simply having a compiler replace (a-b)+b with a is a bug. Simply flushing an internal 80-bit floating register to a 64 bit memory place, then loading it, causes differences in computations.
Note the C++ spec specifically states it does not require an underlying representation or even any certain level of accuracy (22.214.171.124). So many of these bugs are allowed.
The spec does not even require that compile time evaluation of an expression must match a runtime evaluation of the
same expression (8.20.6). When you hit that in code you're going to be surprised.
C/C++ is not required to round-trip floats/doubles when printed and parsed. stream formatting rounding modes are allowed (and are) not exact, leading to different behavior on formatting the same number on different systems.
Dealing with these led to compiler implementation defined behavior added due to market pressures, making C/C++ numerics less of a mess, not anything in the spec.
C/C++ has notoriously been bad at all this, which is why the history of it is littered with such bugs, errors, undefined behaviors, and dozens of compiler switches to try and mitigate such behavior.
For example, many compilers notoriously replaced high-precision Kahan summation with low-precision regular summation because optimizations tell the compiler that such things are allowed.
The state of the art in C/C++ is now passable, but it is still flawed, and has been historically very bad.
And all this met the C/C++ spec of the time. Because if the underlying representation does not require IEEE 754, then it cannot require only transformations for IEEE 754 without making other representations behave badly.