Order of operations and rounding for microcontrollers

This is not a compiler issue: doing the division first here is the legal behaviour, as division and multiplication have equal precedence and are evaluated left-to-right. (Also, when in doubt: use parentheses; there's no penalty.)

You are working with integers, so reading / 0xFFFF will always evaluate to 0 if reading is a uint16_t, unless reading == 0xFFFF.

If you want to use integers only, force the multiplications to be done first by using something like (reading * 10000) / 0xFFFF and make sure both the intermediate result (reading * 10000) and the result fit in the available bits (use uint32_t for such calculations).

Note that on MCUs without an FPU floating-point arithmetic is very slow and best avoided.


This is a fundamental C issue: you need to be extremely clear whether you're doing integer or floating-point arithmetic.

 uint16_t temperature = reading*0.076295;

That promotes "reading" to "float", because 0.076295 is a float literal, then does the multiplication.

uint16_t temperature = reading/0xFFFF*2.5*1000*2;

The first two elements are integers, so the division is done as integer. Then it's promoted to float.

uint16_t temperature = ((float)reading)/0xFFFF*2.5*1000*2;
uint16_t temperature = reading/((float)0xFFFF)*2.5*1000*2;

Either of those two ought to work, and makes the arithmetic explicit.

Note that on some microcontroller architectures, the floating point arithmetic may take much longer than the integer arithmetic. Try it on godbolt: you'll see that it's implemented as function calls rather than CPU instructions.


In C, operators at the same level of precedence are evaluated in left-to-right order. So, in your first equation the division is done first.

As a general rule in integer arithmetic you should try to perform the multiplications first, while avoiding overflow. Do the division last.

But you have other concerns here. If you are using a 16-bit ADC then you should use uint32_t types for the computations. If you use a uint16_t and then divide by 0xFFFF you will never get anything other than 0x0001 or 0x0000. Also, you should be dividing by \$2^{16}\$ rather than \$2^{16}-1\$, which can be accomplished by a right shift of 16 bits if multiplication is expensive on your processor.

Tags:

C

Math

Arm

Compiler