Hello,I'm currently experiencing a problem with Keil ARM compiler on a STM32 target with hw FP unit. I can not explain the reason for this and I am looking forward to any explanation and possibility to avoid if possible.Please find below a code snippet which shows the problem.The first calculation gives a slightly different result to the two other ones. Although all values involved as well as the result should be lossless representable as a float number, result1 is slightly inprecise.
volatile u32 million = 1000000;
volatile float thousand = 1000.0f;
volatile float result1, result2, result3;
result1 = (float)million / 1000.0f; // -> 1000.00006
result2 = (float)1000000 / 1000.0f; // -> 1000
result3 = (float)million / thousand; // -> 1000
Hello Thomas,
Though I don't have hardware to test on, I did a quick test using the supplied FVPs, and can replicate your results (using Arm Compiler for Embedded 6.22).
#include <stdio.h> void float_test(void){ volatile unsigned int million = 1000000; volatile float thousand = 1000.0f; volatile float result1, result2, result3; result1 = (float)million / 1000.0f; // -> 1000.00006 result2 = (float)1000000 / 1000.0f; // -> 1000 result3 = (float)million / thousand; // -> 1000 printf("Million / 1000.0 = %f\n", result1); printf("1000000 / 1000.0 = %f\n", result2); printf("Million / Thousand = %f\n", result3); return; }
Million / 1000.0 = 1000.000061 1000000 / 1000.0 = 1000.000000 Million / Thousand = 1000.000000
Looking at the low level code, I see a difference... for the first calculation (that resulting in slightly inaccurate result), the compiler used VMUL.F32 instruction to multiply 10^6 by 10^-3, whereas when thousand was used as a variable, the VDIV.F32 divide instruction was used.That is likely the cause of the lack of precision.
Hello Ronan,thank you for taking the time to take a look at the problem I described.You're right with your conclusion that VMUL versus VDIV does make a difference for this specific problem. In the time between I took a further look at the numbers and I also realized that whereas the numbers thousand and million can be represented lossless as float, 0.001 can not which unfortunately affects the result of the calculation. The compiler most probably chose to multiply instead of divide for reasons of performance.I enhanced the example code by the calculation of Million * 0.001 which shows the same but equal lack of precision for all cases, just for clarification.
void float_test(void){ volatile unsigned int million = 1000000; volatile float thousand = 1000.0f; volatile float reciprocthousand = 0.001f; volatile float DIVresult1, DIVresult2, DIVresult3; volatile float MULresult1, MULresult2, MULresult3; DIVresult1 = (float)million / 1000.0f; // -> 1000.000061 DIVresult2 = (float)1000000 / 1000.0f; // -> 1000.000000 DIVresult3 = (float)million / thousand; // -> 1000.000000 MULresult1 = (float)million * 0.001f; // -> 1000.000061 MULresult2 = (float)1000000 * 0.001f; // -> 1000.000061 MULresult3 = (float)million * reciprocthousand; // -> 1000.000061; printf("\n\r"); printf("Million / 1000.0 = %f\n\r", DIVresult1); printf("1000000 / 1000.0 = %f\n\r", DIVresult2); printf("Million / Thousand = %f\r\n", DIVresult3); printf("\n\r"); printf("Million * 0.001 = %f\n\r", MULresult1); printf("1000000 * 0.001 = %f\n\r", MULresult2); printf("Million * ReciprocThousand = %f\r\n", MULresult3); return; }
Output :
Million / 1000.0 = 1000.000061 1000000 / 1000.0 = 1000.000000 Million / Thousand = 1000.000000 Million * 0.001 = 1000.000061 1000000 * 0.001 = 1000.000061 Million * ReciprocThousand = 1000.000061