Hi everyone!
Just for fun I tried a few implementations of calculating a percentage of an ADC in relation to it's maximum value of 65535. Normally, whenever possible, I try to avoid floating point calculations and use integer ones instead. But in most cases I'm fine with 32 bit ones. It is faster and generates less code, but you have to be OK with a larger calculation error.
64 bit integers are data types I do not use very often (I cannot remember when I used one), but in order to get higher precision I just wanted to know if the 64 bit calculation is still faster and smaller than the float one, but I wouldn't have expected the result I got.
This is my simple calculation in this example:
16 bit ADC, so a maximum value of 65535.
No I read an ADC value of 38205 and want to calculate the percentage of the full ADC span, so one would calculate:
(38205 * 100%) / 65535 = 58.2970931....%
Then I want to recalculate the original value from this percentage:
((58.2970931% * 65535) / 100% = 38205
Here are three ways for the calculation (without any math library, just to see the difference) - optimization is completely off:
Variant A:
// Calculation in 32 bit integer // 10000 means 100.00% uint32_t adc_result = 38205; uint32_t test; test = ((adc_result * 10000) / 65535); // Results in 5829 - means 58.29% test = ((test * 65535) / 10000); // Results in 38200 - loss of 5
My existing code with this calculation generates 12.184 bytes of code, 34 bytes of data and 414 bytes of RAM.
The execution of these two lines needs 1.267 clock cycles.
Variant B:
// Calculation in float uint32_t adc_result = 38205; float test; test = ((adc_result * 100.0) / 65535); // Results in 58.29709 test = ((test * 65535) / 100.0); // Results in 38205.0 - no loss
My existing code with this calculation generates 12.376 bytes (+ 192) of code, 34 bytes of data and 414 bytes of RAM.
The execution of these two lines needs 12.306 (almost 10x more than 32 bit integer) clock cycles.
Variant C:
// Calculation in 64 bit integer // 100000 means 100.000% uint32_t adc_result = 38205; uint64_t test; test = ((adc_result * 100000) / 65535); // Results in 58297 - means 58.297% test = ((test * 65535) / 100000); // Results in 38204 - loss of 1
My existing code with this calculation generates 13.624 bytes (+ 1248 compared to float) of code, 34 bytes of data and 414 bytes of RAM.
The execution of these two lines needs 19.297 (+ 6991 compared to float) clock cycles.
For all three types of calculation you can improve the speed slightly by using 65536 instead of 65535 and write a bit shift by 16, but the relation between the calculations stays the same.
In the last example, when declaring adc_result as uint64_t as well, the code size gets 13.656 bytes and the execution time is 37.410 clock cycles!
Honestly I wouldn't have expected the result of the 64 bit calculation to be that slow (and also that large in code size). And since I'm not that deep in how this calculation is done internally, I was hoping that someone could tell me why 64 bit takes so much longer.
Most of the clock cycles go into the second line of the calculation.
Dennis