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.

float divide function call abnormal in compiler 7.4.15

Other Parts Discussed in Thread: TMS320C6657

Hi Expert,

Traget: TMS320C6657,6678, all C66 core dsp can reproduce this issue.

Compiler version: 7.4.15

When my customer do optimziation, they used below 2 codes and gets different cycle running result.

float FREQ_UAC_L1 = 10.0

float tmp_interval;

//tmp_interval = 500.0/FREQ_UAC_L1 ; case1

//tmp_interval = 500/FREQ_UAC_L1 +; case2

case1 have more thhan 1x cycle consumption than case2,

after read assmbly file, we found that in case 2 , the compiler calls __c6xabi_divf, which should be right

but for case1 , it calls__c6xabi_divd, which is integer divide and have low performance than __c6xabi_divf, 2x performance down.

It is easy to reproduce the issue based on above code, could you help check it?

Any more inputs please let me know

  • Thomas Yang55737 said:
    //tmp_interval = 500.0/FREQ_UAC_L1 ; case1

    The unsuffixed floating point constant 500.0 is implicitly a double-precision constant, which as per the C standard causes the compiler to perform a double-precision division.

    By adding a "f" suffix the constant is explicitly single precision, which should make the compiler perform a single-precision division (and for consistency also consider initializing FREQ_UAC_L1 with a single-precision constant):

    float FREQ_UAC_L1 = 10.0f;
    float tmp_interval;
    
    tmp_interval = 500.0f/FREQ_UAC_L1;

  • Consider using the option --float_operations_allowed=32 to find expressions which unexpectedly use type double.  Read more about that option in the C6000 compiler manual.

    Thanks and regards,

    -George

  • George,

    After more test,  it seems that the rules that compiler call divd or divf for caculation  500.0/float is random, please see below test.

    call divf:

     

    call divd:

    Customer would like to know the rules and would you have some comments on this?

    Thanks

    -Thomas

  • That appears to be an optimization. Given that x and y are floats and 500.0 is exactly representable as float, computing 500.0 / x (in double precision) would yield the same result as 500.0f / x (i.e., in single precision), only with a little more precision. Now because the result is assigned to another float, it needs to be converted to float anyway. The conversion is implementation-defined, and the implementation seems to know that computing 500.0 / x in double precision and then converting to float yields the same result as directly computing 500.0 / x in single precision. The latter is cheaper.


    The second case is different, because y needs to be added to the result of 500.0 / x, and that addition needs to be performed in double precision. Therefore, a double precision result of 500.0 / x is needed anyway (and an early conversion to float could yield wrong results).


    Just my two cents

    Markus

  • Thomas Yang55737 said:
    After more test,  it seems that the rules that compiler call divd or divf for caculation  500.0/float is random, please see below test.

    As I tried to explain previously, the constant 500.0 is explicitly a double-precision constant, and so I would expect a standard conforming C compiler to use a double precision division for 500.0/float.

    For the compiler to use single precision calculations the constants should be explicitly single precision, by adding a "f" suffix to the constants.

    As an example the following program was compiled with the C6000 compiler v7.4.15:

    float implicit_double_test_a (float x, float y)
    {
        float ret;
    
        ret = 500.0 / x;
        ret += y;
    
        return ret;
    }
    
    float implicit_double_test_b (float x, float y)
    {
        float ret;
    
        ret = (500.0 / x) + y;
    
        return ret;
    }
    
    
    float explicit_single_test_a (float x, float y)
    {
        float ret;
    
        ret = 500.0f / x;
        ret += y;
    
        return ret;
    }
    
    float explicit_single_test_b (float x, float y)
    {
        float ret;
    
        ret = (500.0f / x) + y;
    
        return ret;
    }
    
    int main(void)
    {
        float x = 1.2345f;
        float y = 2.3456f;
    
        return implicit_double_test_a (x, y) +
               implicit_double_test_b (x, y) +
               explicit_single_test_a (x, y) +
               explicit_single_test_b (x ,y);
    }
    

    The --keep_asm option was used to save the generated assembler. The generated code for the functions shows the following, with my understanding of what is expected:

    implicit_double_test_a : Uses __c6xabi_divf. Expected __c6xabi_divd to be used as a double precision constant is used

    implicit_double_test_b : Uses __c6xabi_divd, which is the expected result

    explicit_single_test_a : Uses __c6xabi_divf, which is the expected result

    explicit_single_test_b : Uses __c6xabi_divf, which is the expected result

    The CCS 6.1.1 project used is attached TMS320C6657_floating_point_divide.zip. The Debug\main.asm and Relase\main.asm contain the generated assembler, using the default CCS debug (no optimization) and release (optimization level 3) settings.

  • Chester Gillon said:

    As I tried to explain previously, the constant 500.0 is explicitly a double-precision constant, and so I would expect a standard conforming C compiler to use a double precision division for 500.0/float.

    Yes, but implementations only need to care about observable behavior.

    Chester Gillon said:

    float implicit_double_test_a (float x, float y) { float ret; ret = 500.0 / x; ret += y; return ret; }

    [...]

    implicit_double_test_a : Uses __c6xabi_divf. Expected __c6xabi_divd to be used as a double precision constant is used

    As I said, I believe the compiler is clever here. It sees that __c6xabi_divf(500.0f, x) produces exactly the same result as (float)__c6xabi_divd(500.0, (double)x), which it "should" compute. As the former is cheaper, it chooses to do all operations in single precision (unless the results differ, in which case it really is a compiler bug).

    Markus

  • Markus Moll said:
    As I said, I believe the compiler is clever here. It sees that __c6xabi_divf(500.0f, x) produces exactly the same result as (float)__c6xabi_divd(500.0, (double)x), which it "should" compute. As the former is cheaper, it chooses to do all operations in single precision (unless the results differ, in which case it really is a compiler bug).

    By changing the constants I see what you mean. If I change the test program from using 500.0 / 500.0f to 500.1 / 500.1f then:

    - Both implicit_double_test_a() and implicit_double_test_b() call __c6xabi_divd

    - Both explicit_single_test_a() and explicit_single_test_b() call __c6xabi_divf

    Hopefully this answers the original question about how to ensure the compiler calls __c6xabi_divf rather than __c6xabi_divd

  • Markus and Chester are correct here. An unsuffixed float constant is a double, so the expression must be computed as a double. However, the compiler will optimize double expressions to float when it can show that the result will be the same; typically this is due to an assignment to a variable of type float.