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!

  • This caught my eye:

    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;

    You're assigning a float pointer to a float and then passing address of the float to your function, shouldn't the assignment be 

    float32_t input_param = *input_raw; //?

    I'd like to see the sources of the filter functions, maybe there's something similar where pointed value and pointer are mixed? Doesn't the compiler warn about the assignment?

  • Hi Otso

    Thanks for the reply! I 'll check the code and try what you suggested because now I'm not quite sure if what you are suggesting is actually right or if I forgot to completely change the example back to the pointer version. But you are right about the code as it is written above.

    Thanks again

    Michiel

  • So I checked the code and ran the examples again.

    This case does not work properly and jumps to the aeabi_f2d code (seems to convert float32 to float64) I have no idea why to be honest:

    void dsp_algorithm(float32_t *pIncomingSample, uint32_t counter){
      float32_t intermediate = *pIncomingSample;
      turn_gyry(*pIncomingSample);
    }
    
    turn_gyr_y.c:
    
    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);
    
      previous_output_der = &output_derivative;
    
      printf("output low pass %f", output_low_pass );
      printf("output derivative %f", output_derivative);
    }
    
    turn_gyr_y.h:
    void turn_gyry(float32_t input_raw);
    
    
    

    As you can see in the example above we pass the data as a dereference of a pointer to a float32 to a function that expects a float32. The compiler does not complain about this at all since all the types match. Also when I add a watch point for the variable intermediate or the dereference of pIncomingSample the value is exactly what I expect it to be. I can step through the code and see it get "messed up" by the f2d stuff.

    Now if I change the code to:

    file algo.c:
    
    void dsp_algorithm(float32_t *pIncomingSample, uint32_t counter){
      float32_t intermediate = *pIncomingSample;
      turn_gyry(pIncomingSample);
    
    }
    
    file turn_gyr_y.h:
    
    void turn_gyry(float32_t *input_raw);
    
    file turn_gyr_y.c:
    
    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);
    
      previous_output_der = &output_derivative;
    
      printf("output low pass %f", output_low_pass );
      printf("output derivative %f", output_derivative);
    }
    
    

    So in this case we pass the input to the turn_gyry function as a pointer. Now if I check the float32 value in the variable input_param it is passed just fine. I guess my question boils down to: Is it possible that passing float32 values by value is for some reason buggy or faulty while passing them by reference works just fine? The cmsis examples also seem to pass floats by reference but still, in case we do not want to alter the values passed to a function we should be able to pass the value of a variable and not a pointer to the actual variable... I wonder if this is a known issue or if we are missing something.

    Best regards,

    Michiel

  • Hello,

    I can't find any fault in this code either. Things which come to my mind:

    • I'm assuming that this isn't obfuscated C contest where float32_t is misdefined.
    • float32_t intermediate is not used anywhere, delete it?
    • Which optimization setting was used? Is there a difference between no optimization, optimize for size, optimize for speed?
    • Could parameters be made const? e.g. const float32_t * const input_raw.
    • Without seeing the filter function sources, it's not possible to be sure that problem is not in them. 
    • I'm assuming that filter functions return void, even though the return value should not matter.
    • I'm assuming there's no issues with interrupts, memory access violations etc.

    I've been passing floats around as values and by reference and I've never had issue like this, on the other hand I use float rather than float32_t. Then again it should not make any difference on nRF52 chips, as:

    nRF5_SDK_15.3.0_59ac345/components/toolchain/cmsis/include/arm_math.h:283:  typedef float float32_t;

  • 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

     

Related