This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Passing a float by value or reference

Hi,

We are seeing some strange stuff when trying to pass floating point values to a function. When we pass the parameter by value (meaning as an actual floating point number) the parameters' value changes. When we debug the code we can see that in the disassembly there is a call too __aeabi_f2d. However there should be no reason to call this since all of the values are floats and not doubles. When we pass the floating point number by reference everything works fine.

We are using the nRF52840 with the gcc compiler that comes with the 4.20a version of segger embedded studio.

Meaning this does not work, the value of variable intermediate is not the same value as the value found in input_param :

  float32_t intermediate = *pIncomingSample;
  turn_gyry(intermediate);

void turn_gyry(float32_t input_raw){
  float32_t output_low_pass; 
  float32_t output_moving_average;
  float32_t output_derivative;
  float32_t input_param = input_raw;

  filter_turn_gyry_low_pass(&input_param, &output_low_pass);
  filter_turn_gyry_moving_average(&output_low_pass, &output_moving_average);
  filter_turn_gyry_derivative(&output_moving_average, &output_derivative);

  printf("output low pass %f", output_low_pass );
  printf("output derivative %f", output_derivative);
}

But this works just fine:

  float32_t intermediate = *pIncomingSample;
  turn_gyry(pIncomingSample);

//the function definition of turn_gyry
void turn_gyry(float32_t *input_raw){
  float32_t output_low_pass; 
  float32_t output_moving_average;
  float32_t output_derivative;
  float32_t input_param = input_raw;

  filter_turn_gyry_low_pass(&input_param, &output_low_pass);
  filter_turn_gyry_moving_average(&output_low_pass, &output_moving_average);
  filter_turn_gyry_derivative(&output_moving_average, &output_derivative);

  printf("output low pass %f", output_low_pass );
  printf("output derivative %f", output_derivative);
}

Does anybody have an explanation for this behavior? I believe that the call to __aeabi_f2d implies that for some reason the compiler thinks he needs to convert a float to a double but there is no reason to do this...

Any ideas are welcome

Thanks!

Parents
  • Hi!

    This is not some sort of obfuscated C contest Smiley. float32_t is defined as you mention it in the arm_math.h file.

    I added the intermediate only after we noticed the problem to see if it would be assigned properly and to add a watch point. The same for input_param for the matter. Removing them does not do anything to resolve the problem but you are right they are not useful.

    Right now the filter code is just basically a call to the cmsis lib fir filter:

    void filter_turn_gyry_low_pass(float32_t *p_input, float32_t *p_output){ 
    
      arm_fir_f32(&S, p_input, p_output, blockSize);
    
    }

    The example data used for this test is defined as a const float32_t. In non-test situations this data will not come from a const array, but from sensors so changing them to const is not really an option. But anyway in this case the variables come form a const array. Do you ask this because of memory corruption reasons or some other mechanism?

    I'll try to do some tests with different optimization levels! But in this case it was set to debug in SES which should not be very aggressive optimization I assume. But this might be interesting to try even tough the data is const so I don't exactly know what would be optimized for.

    As for the interrupts, we tried removing most of them for testing, there is no BLE active, no watchdogs, no Timers (other than the timer that calls the filter periodically). Basically we tried to limit the parts that could influence this code as much as possible for testing. Anyway I get no interrupts interrupting this code when I run it and also how would we then explain that the pointer version of the code is working just fine?

    Anyway thanks for engaging in this discussion. I will try the optimization suggestion and report back. I might also try passing some floats in some sort of dummy functions to check if these behave in the same way.

    Best regards

    Michiel

     

Reply
  • Hi!

    This is not some sort of obfuscated C contest Smiley. float32_t is defined as you mention it in the arm_math.h file.

    I added the intermediate only after we noticed the problem to see if it would be assigned properly and to add a watch point. The same for input_param for the matter. Removing them does not do anything to resolve the problem but you are right they are not useful.

    Right now the filter code is just basically a call to the cmsis lib fir filter:

    void filter_turn_gyry_low_pass(float32_t *p_input, float32_t *p_output){ 
    
      arm_fir_f32(&S, p_input, p_output, blockSize);
    
    }

    The example data used for this test is defined as a const float32_t. In non-test situations this data will not come from a const array, but from sensors so changing them to const is not really an option. But anyway in this case the variables come form a const array. Do you ask this because of memory corruption reasons or some other mechanism?

    I'll try to do some tests with different optimization levels! But in this case it was set to debug in SES which should not be very aggressive optimization I assume. But this might be interesting to try even tough the data is const so I don't exactly know what would be optimized for.

    As for the interrupts, we tried removing most of them for testing, there is no BLE active, no watchdogs, no Timers (other than the timer that calls the filter periodically). Basically we tried to limit the parts that could influence this code as much as possible for testing. Anyway I get no interrupts interrupting this code when I run it and also how would we then explain that the pointer version of the code is working just fine?

    Anyway thanks for engaging in this discussion. I will try the optimization suggestion and report back. I might also try passing some floats in some sort of dummy functions to check if these behave in the same way.

    Best regards

    Michiel

     

Children
  • Hello, 

    What is your blockSize? If you pass the address of a first element of an array the behavior is different than if you pass address of one intermediate variable. The pass-by-value case would be valid only if blockSize is 1, and it would probably not make sense for FIR filtering. 

    The example data used for this test is defined as a const float32_t. In non-test situations this data will not come from a const array, but from sensors so changing them to const is not really an option. But anyway in this case the variables come form a const array. Do you ask this because of memory corruption reasons or some other mechanism?

    Declaring variables as const whenever possible prevents a whole lot of bugs especially when there's a team working on a codebase. I think you should get a compiler warning if you try to pass a non-const array to the arm_fir_f32 in any case.

    As for the interrupts, we tried removing most of them for testing, there is no BLE active, no watchdogs, no Timers (other than the timer that calls the filter periodically). Basically we tried to limit the parts that could influence this code as much as possible for testing. Anyway I get no interrupts interrupting this code when I run it and also how would we then explain that the pointer version of the code is working just fine?

    Most of the bugs I encounter are coming from invalid assumptions. That's why I always stop to think about what my assumptions are and say them out loud. One reason why the code could be working just fine with pointer and break when using stack allocated variable is that an interrupt would be corrupting the stack somehow. My guess is on the blockSize though.

Related