Hello,
My function calculates scalar value from dB value using formular scalar = 10 ^ (dB / 20)
In case of ARMCLANG V6.12 and Optimizer Level -O2 the calculation fails as it is shown in my test code.
Maybe, I overlook something, do I?
#include "stm32f4xx.h" #include <stdio.h> #include <stdint.h> #include <math.h> static volatile int8_t dB; static int8_t lastGain; /**/ void setGain(int8_t gain) { float vol; if (lastGain == gain) { // value is already set return; } // calculate scalar value vol = powf(10.f, (float)gain / 20.f); // = 0.199526... // check limits if (vol > 10) { // "vol out of range" is printed in case of ARMCLANG V6.12 and Optimization > -O2 printf("vol out of range\n"); } else { // "passed" is printed in case of ARMCLANG V6.12 and Optimization -O0 or -O1 printf("passed\n"); lastGain = gain; } } /**/ int main(void) { dB = -14; for(;;) { setGain(dB); } }
I have quickly looked into this and I can replicate the behavior (either on target or simulator).
It looks like the compiler optimization incorrectly converts "(float)gain" into 242.0f instead of -14.0f.
"gain" value is 0xFFFFFFF2 (-14) on function setGain entry which is correct. However due to the comparison with lastGain the value gets truncated to 0x000000F2 and then later reused to convert to float which results in incorrect value 242.0f.
I have created a support ticket for this problem.
"gain" value is 0xFFFFFFF2 (-14) on function setGain entry which is correct.
Actually, if that were the case, it would not be correct. The value of "gain" cannot be that --- because that value is way too large to fit into the int8_t type.
Now, the CPU register holding the value may contain all those bytes, but only one of those can be "gain" itself.
The actual problem then would be a failure to sign-extend this single byte before passing it to a compiler intrinsic function that expects to be fed a 32-bit value.