We've talked about having support for user-defined literals, so that you could have decimal literals; in the meantime, a macro works for that.
Fixed point is where say in a 32 bit word, 16 bits is used for the integer part and 16 bits for the fraction or any combination that you can declare. Usually notated like 16.16 or 16:16. Another format example in 32 bits could be 8.24 and also formats in other word sizes.
I found some crates using 'fixed' as the search term.
https://crates.io/search?page=1&per_page=10&q=fixed%20point
It's been my desire to have a language for embedded to have first class support for fixed point just from the ability to work with them like you would a floating point number and not have to worry about macros.
Along the same lines, is there anything that'd be needed to have "first class support" other than literals? If we had user-defined literal formats, so that one of those fixed-point crates could define a format for fixed-point literals, would that suffice?
Multiplying a Q8.24 with another Q8.24 makes a Q16.48.
In C that's not the same as the '∗' operator; you'd generally cast both arguments to 64-bit first, and rely on the compiler being smart enough to use a 32x32->64 instruction if available instead of 64x64->64. But even that's ambiguous - is it signed or unsigned widening multiplication?
Adding a Q8.8 to a Q4.12 needs a 4-bit shift before the addition. So does adding a Q5.27 to Q1.31.
The right thing to do is, unfortunately, ambiguous.
E.g. when adding Q5.27 to Q1.31, do you need to keep the full range and precision and end up with Q6.31? Or do you right shift the second argument, ignore overflow, and call it a day with a Q5.27 result that is implemented in C as (uint32_t)(a + (b >> 4)? (Though if it's signed, which it usually is, (b >> 4) is dodgy in C anyway as it's implementation-defined.) If you do ignore overflow, is that a saturating addition (like used for audio/graphics) or wraparound (like in C)?
Really, those decisions depend on what the numbers mean, as well as range and precision assumptions the author may already know are valid for numerical reasons. So maybe there's a case for traits on number types which specify whether they are default-saturating, default-precision-maintaining, default-range-maintaining.
Another operation is to change the representation, either to truncate/round some precision bits, reduce range bits (saturating or assuming), or expand range bits prior to doing some sums (because addition doesn't expand the range bits in real implementations, you have to explicitly do that yourself in advance of adding).
The way I see this done in careful DSP in C or C-like languages is as a series of function or macro calls that specify input and output formats, avoiding operators and the ambiguity they imply.
I know there are some TI and ADI arch's with special fixed point data types built into their compilers (which are nonstandard extensions to C/C++, fwiw), but they are unsupported by LLVM last I checked.
I've also heard that those niches are having their lunch eaten by ARM M4/M7 cores which have floating point vector instructions, and you can write Rust to target those today.