atof() giving inconsistent results

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi there,

I have a project on the E70 where I am getting input from an external display over RS232 (USART) as a comma separated string, and would like to parse the values in the string to variables in my program. I am using the DMA to transfer the incoming data to a variable "usart_receive_buffer". The code below is run in a loop every 20 ms.

 

What I am finding is very intermittently (less than 0.1% of the time) but irregularly, atoff() is giving incorrect conversion values. With reference to the snippet below, a breakpoint at error = 1 is hit, even though the first element is set to 0.60. The error is always the same value though, "0.609000027". I find this odd, because "dest_buf1" is always "0.60" (or, {0x30,0x2e,0x36,0x30}) so I don't know where the third decimal place is coming from.

 

I guess there is something wrong elsewhere in my project, because if I put this code into its own project and run it in the while loop, the break point is never hit.

 

I tried eliminating the possibility that the DMA was changing the string while all this was going on by just setting the string to the same value every loop, but that did not change things. 

 

Does anyone have any ideas how I can diagnose this problem?

 

char dest_buf1[4];
char dest_buf2[4];
float float_buf1, float_buf2;
strcpy(usart_receive_data, usart_receive_buffer, 16);     // First attempt
strcpy(usart_receive_data, "0.60,1.00,1.00\0", 16);       // Removed usart_receive_buffer reference to test
if (1) {
    strncpy(dest_buf1, &usart_receive_data[0], 4);
    float_buf1 = atoff(dest_buf1);
    if ((float_buf1 > 0.0) && (float_buf1 < 1.1)) rcv1 = (float)float_buf1;
    //
    strncpy(dest_buf2, &usart_receive_data[5], 4);
    float_buf2 = atoff(dest_buf2);
    if ((float_buf2 > 0.9) && (float_buf2 < 1.1)) rcv2 = (float)float_buf2;
}
if (rcv1 > 0.6001) {
    volatile int error = 1;   // BREAK POINT HERE.
}

 

This topic has a solution.
Last Edited: Mon. Mar 23, 2020 - 10:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sounds like the conversion of a decimal number to float is inexact - this is a common problem with floating point. There are online ieee754 floating point sites where you can observe the issue and understand the workings of floating point representation.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did think about that but 0.6 as a float is 0.60000002384185791015625, a much smaller error compared with 0.609 that I am getting.

 

To put it another way, the value I expect is stored as 0x3f19999a, but the value I am getting is stored as 0x3f1be76d. 

 

Also worth noting that using doubles instead of floats does not solve the issue.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On an AVR, double and float are both 32-bit (if I remember correctly), so double does not add any precision.

 

Edit: That is the AVR-GCC compiler (and I did not notice this was for an ARM...)

David (aka frog_jr)

Last Edited: Sun. Mar 22, 2020 - 11:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't know that, that is interesting though. Thank you!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With dma, do you invalidate the cache before reading the data? What the debugger sees vs what the processor sees may be quite different. That doesn’t explain your example code. If the fpu is enabled, do the isrs preserve the fpu state?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, so I think the issue is that dest_buf1 is not null terminated (or terminated at all). I suspect atof() does not stop at the end of the variable it has been provided but continues as long as it runs into a non-digit character.

 

In my case there was data in the adjacent memory. Often the adjacent data was not a digit, but sometimes it was which explains the intermittent nature of the error and the fact that the output was always 0.60xxxx.

 

Increasing the size of the buffer to include the comma from the input string appears to have fixed the issue.

 

If the fpu is enabled, do the isrs preserve the fpu state?

 The FPU is enabled but I have no idea if the state is preserved. Can you please tell me how I can determine this, for my information?

Last Edited: Mon. Mar 23, 2020 - 09:39 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

About strncpy

Warning: If there is no null byte among the first n bytes of src, the string  placed
       in dest will not be null-terminated.
 

 So (if your actual code is anything like the example) it all depends on if by chance some digit is in memory after your buffer. For your example you might be able to force the problem by changing the order:

 

    strncpy(dest_buf2, &usart_receive_data[5], 4);
    float_buf2 = atoff(dest_buf2);
    if ((float_buf2 > 0.9) && (float_buf2 < 1.1)) rcv2 = (float)float_buf2;

    strncpy(dest_buf1, &usart_receive_data[0], 4);
    float_buf1 = atoff(dest_buf1);
    if ((float_buf1 > 0.0) && (float_buf1 < 1.1)) rcv1 = (float)float_buf1;

 
I expect rcv1 can be 0.601 then (atoff is parsing also the first character '1' from dest_buf2). To fix this:
 

char dest_buf1[4];

needs to be bigger to have room for the terminator which you have to add:

 

char dest_buf1[5];
dest_buf1[4] = '\0';

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you Lars! I think this is the correct solution.