Is there a bug in strtof ? Or a bug in my code ?

28 May 2013

I'm reading floats in from a file using strtof :

int main() {
FILE * fp;
char szBuffer[256];
char * pEnd;
float lat, lon;

    pc.baud(PCBAUD);

    fp = fopen("/sd/test/waypoints.txt", "r");
    if(fp == NULL) { error("Could not open file for reading\r\n"); }
    
    while(fgets (szBuffer, 256, fp) != NULL){
        szBuffer[strlen(szBuffer)-1] = 0;
        pc.printf("String = \"%s\" \r\n", szBuffer);
        
        lat = strtof (szBuffer, &pEnd);
        if(pEnd[0]==','){ pEnd[0]=' '; }
        lon = strtof (pEnd, NULL);
        pc.printf("LAT = \"%.12f\" LON=\"%.12f\" \r\n", lat, lon);

        }
    fclose(fp);
}

I get :

     String = "54.649595,-1.807532" 
     LAT = "54.649593353271" LON="-1.807531952858" 
     String = "55.778595,-2.834532" 
     LAT = "55.778594970703" LON="-2.834532022476" 
     String = "51.464662,0.376506" 
     LAT = "51.464660644531" LON="0.376506000757" 
     String = "10.00000,120.01" 
     LAT = "10.000000000000" LON="120.010002136230" 
     String = "-1234.5678,1234.56780000" 
     LAT = "-1234.567749023438" LON="1234.567749023438"

The file is a unix format text file and just contains :

     54.649595,-1.807532
     55.778595,-2.834532
     51.464662,0.376506
     10.00000,120.01
     -1234.5678,1234.56780000

How can 120.01 from the file become 120.010002136230 when converted to a float ?!

Am I doing something wrong or is there a bug in strtof ?

Thanks.

Jon.

28 May 2013

This is probably a rounding issue. You provide a string as float number. The strtof needs to convert this string to an actual floating point value according to IEEE 32 bit floating point formats. These numbers are basically summations of binary fractional numbers (1/2, 1/4, 1/8, 1/16 etc) and a binary exponent. Your string float is rounded to the nearest binary value using 32 bits in total. That leads to a rounding error for most numbers. When you print this binary value using 12 decimals (formatter %.12f) the rounding error shows up. You could use doubles (64bit) to reduce the error or just ignore it and print less decimals :).