Converting Between Float and Fixed-Point with an ESP32
TL;WR: You can use FLOOR.S
, CIEL.S
, ROUND.S
, FLOAT.S
, and UFLOAT.S
in a single inline-assembly instruction to convert between fixed-point and float values.
Example implementations at bottom of page.
There are alternate double versions of the instructions in the xtensa ISA but they are not implemented in the ESP32 tensilica cores.
You can find the full xtensa assembly instruction set documentation on the Cadence website https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/ip/tensilica-ip/isa-summary.pdf.
If you’re writing code that uses floating point numbers you’re already using some of these instructions. The toolchain will use them whenever you convert between int
/uint
and float
types.
The instructions have a third parameter which specifies how many bits come after the decimal point (fractional bits). A value of 1
would be ±0.5 in the last bit, 2
would be ±0.25 in the last two bits, etc.
The normal assembly emitted when doing something like float f = (int)53
will have it set to 0 (no fractional bits), but by calling the instruction manually you can specify the precision.
The fractional bits parameter is a 0..15 constant and cannot be specified at runtime, so if you need different shapes you would just implement as separate methods, templates, or overloading methods with custom types.
For example, the instruction FLOAT.S f0, a2, 0
with 53
in register a2
would convert 53
to 53.0f
in the f0
register. However the instruction FLOAT.S f0, a2, 1
would convert it to 26.5f
.
Fractional Bits | Example Instruction | Input Binary | Input Int | Result Float |
---|---|---|---|---|
0 (normal float<>int) | FLOAT.S f*n*, a*n*, 0 |
0b00110100 | 52 | 52.0 |
0 (normal float<>int) | FLOAT.S f*n*, a*n*, 0 |
0b00110101 | 53 | 53.0 |
1 (lsb is ±0.5) | FLOAT.S f*n*, a*n*, 1 |
0b00110100 | 52 | 26.0 |
1 (lsb is ±0.5) | FLOAT.S f*n*, a*n*, 1 |
0b00110101 | 53 | 26.5 |
2 (lsb is ±0.25) | FLOAT.S f*n*, a*n*, 2 |
0b00110100 | 52 | 13.0 |
2 (lsb is ±0.25) | FLOAT.S f*n*, a*n*, 2 |
0b00110101 | 53 | 13.25 |
2 (lsb is ±0.25) | FLOAT.S f*n*, a*n*, 2 |
0b00110110 | 54 | 13.50 |
The FLOOR.S
, CIEL.S
, and ROUND.S
instructions convert float to fixed-point by the method in their name. Since fixed-point can’t try to store arbitrary real numbers you are telling the processor how you’d like the number to be butchered. The FLOAT.S
and UFLOAT.S
instructions just use the default rounding/inf/NaN settings to convert fixed-point to float.