There's also BCD (binary coded decimal) that can solve some problems by avoiding the decimal-to-binary conversions if you're mainly dealing with decimal values. For instance 0.2 can't usually be represented in binary but of course it poses no problem in BCD.
It is more common these days to use base-1000, instead, when you need exact decimal representations. You can fit three base-1000 "digits" in a 32-bit word, with two bits left over for sign plus any other flag you find useful. (One such use could be to make a zero in the second place indicate that the rest of the word is actually binary; then regular arithmetic works on such words.) Calculations in base-1000 are quite a lot faster than BCD.
Almost always when people think they need decimal, binary -- even binary floating-point, if the numbers are small enough -- is much, much better. Just be sure to represent everything as an integer number of the smallest unit, say pennies; and scale (*100, /100) on I/O.
The chances of being able to run deterministic floating point calculations across this stack is basically zero (even leaving aside that the games are often run on ARM chips), and so we use this library when floats are absolutely necessary (but more often just plain longs):
It is a little terrifying that e.g. normalizing a vector involves a while loop, but all things considered the whole thing runs surprisingly well.
(I agree with everything in your post, just thought I could add a real world field report)
Our game would snapshot the entire game state every few seconds and send that back to server to detect desyncs and cheaters. Floating point math, to our astonishment, was not the source of any non-determinism.
I'm 80% sure that only source of non-determinism we encountered were from trig functions, so we just hard-coded lookup tables.
2: It was 2012 when we started.
However, complicated calculations or anything involving angles or other math functions quickly becomes more convenient when expressed as a Fix64, which is more or less a drop in replacement for float.
I would ideally use Fix64 everywhere, but given the torturous route the C# takes to be transformed into something that's executed on the client machines, my faith in the compiler's ability to generate good code for that is basically zero. I mentally treat long + long as a single instruction, but Fix64 + Fix64 as a function call.
Even something simple like multiplying up and dividing down quickly adds a lot of overhead, and when running on mobiles you really need all the speed you can get.
BCD is/was super common in measurement equipment for internal calculations for this reason, and also because it is trivial to format for display (LED/LCD/VFDs) or text output (bus system, printer/plotter).
The 8086 (and its descendants, of course) supports BCD by having instructions to adjust the result after the basic add/sub/mul/div instructions, though only one byte at a time.
The 6502's add and subtract instructions would operate on, and output, BCD values if the special purpose "decimal" flag was set. Again only in 8-bit (two digit) chunks but that is to be expected as it was an 8-bit chip generally.