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.

C2000 compiler v6.0.1 and v16.6.0 bug on TMS320F2833x - float to integer conversion

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 ...

  • It is technically undefined behavior to cast a negative float to an unsigned integer (C99 6.3.1.4 paragraph 1). Once you have undefined behavior anywhere in the program, the entire program is illegal C code.

    Nonetheless, it's still probably a bug that demo1 produces a result of 0 for (int)TST_fVar. I'll have a look.
  • 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).