This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
We have noticed incorrect code generation when optimization -O3 switched on. The problem disappears when the optimization is changed to -O0. We use the FPU. When a float value is converted first to unsigned int and then to signed int (or first to signed and then to unsigned), then the second conversion is incorrect. This behaviour was observed both with 6.0.1 and 616.6.0 tools.
* F32TOI32 / F32TOI16 instruction is used for conversion to unsigned int
* F32TOUI32 / F32TOUI16 instruction is used for signed int.
An example
float TST_fVar = -2.0; /* 32 bit float */ int TST_nVar; /* 16 bit signed int */ unstigned int TST_wVar; /* 16 bit unsigned int */
void error_demo1( void ) { TST_wVar = (unsigned int)TST_fVar; /* TST_wVar == 0 (ok) */ TST_nVar = (int)TST_fVar; /* TST_nVar == 0 (!!!) */ }
void error_demo2( void ) { TST_nVar = (int)TST_fVar; /* TST_nVar == -2 (ok) */ TST_wVar = (unsigned int)TST_fVar; /* TST_wVar == 0xFFFD (!!!) */ }
When (float)-2.0 is converted to (int)0, there is no workaround ...
The compiler is correct in this case; there is no bug. The optimizer is noticing that you're converting the float value to both int and unsigned int. The optimizer also knows that it is undefined behavior to convert a float value to an integer value if the value cannot be represented in the target type. The optimizer may legitimately conclude that the value of TST_fVar is in the range [0, 32767]; if it isn't, then the code invokes undefined behavior and is thus not legal C code. Given that the range is [0, 32767], the compiler may use either F32TOI16 or F32TOU16 for the conversion.
If you check the value of TST_fVar before performing the conversion, the compiler does the right thing. Here are some examples to try with different values of f:
#include <limits.h> float f; int i; unsigned u; void f1(void) { u = (unsigned int)f; i = (int)f; } void f2(void) { i = (int)f; u = (unsigned int)f; } void f3(void) { if (f >= 0) u = (unsigned int)f; i = (int)f; } void f4(void) { i = (int)f; if (f >= 0) u = (unsigned int)f; } void f5(void) { u = (unsigned int)f; if (f >= INT_MIN && f <= INT_MAX) i = (int)f; } void f6(void) { if (f >= INT_MIN && f <= INT_MAX) i = (int)f; u = (unsigned int)f; } void f7(void) { if (f >= 0) u = (unsigned int)f; if (f >= INT_MIN && f <= INT_MAX) i = (int)f; } void f8(void) { if (f >= INT_MIN && f <= INT_MAX) i = (int)f; if (f >= 0) u = (unsigned int)f; }
People use languages like Java, because the compiler tools appear to be written rather by lawyers then by technicians using brain. The elementary rule is, that the same operation should give the same results. But the C language authorities evidently love making holes traps. And the C community accepts it, so everything is OK. We can show how clever we are, that we are able to use such an idiotic language. Only the C programs become unpredictable. Also, when you have a time sensitive code, you don't want to make extra tests, when the CPU is able to do correct conversion in one instruction. What is the point of using TI CPUs then? By the way, the function f8 should be:
void f8(void) { if (f <= (float)INT_MIN ) { i = INT_MIN; }
else if( f <= (float)INT_MAX ) { i = (int)f; }
else { i = INT_MAX; }
if (f <= 0) { u = 0U; }
else if( f <= UINT_MAX ) { u = (unsigned int)f; }
else { u = UINT_MAX; }
}
What is the point of using optimizer then? Does there exist some intrinsic for avoiding such a stupidity?
The optimizer may not harm the rules of C language, but it harms the rules of logical thinking. By the way, the C89 definition, which is declared to be implemented by TI is absolutely unavailable. This is Head XXII, if you know what I mean.
If you know TST_fVar is in the range [INT_MIN, INT_MAX], cast it to int and never unsigned int, and it will behave as expected.
If you have an unknown float value, for C28x you can saturate it to a known range like so: __fsat(TST_fVar, INT_MAX, INT_MIN).