Bumzur
Bumzur

Reputation: 111

Float prints as NaN, but prints correct value immediately afterwards

I've been having some weird problems with floats being printed as NaN depending on where in my code I print them. For context, the code sorts a list of earthquake data using a BST, and then loops through the sorted data to find the largest gap between two consecutive earthquakes. Those two earthquakes are then printed here:

FILE* output_file = safe_open_file("task_1_bst.csv", "w");
fprintf(output_file, "timestamp,latitude,longitude,magnitude\n");
eq_print(output_file, sorted_arr[longest_index-1]);
fprintf(output_file, "\n");
eq_print(output_file, sorted_arr[longest_index-1]);
fprintf(output_file, "\n");
eq_print(output_file, sorted_arr[longest_index]);
fclose(output_file);

As you can see, I am printing one of the earthquakes twice, and this is the result:

timestamp,latitude,longitude,magnitude
2009-06-13T06:02:52.500Z,nan,-115.392,3.4
2009-06-13T06:02:52.500Z,31.315,-115.392,3.4
2009-06-13T16:04:06.650Z,3.930,126.648,4.4

For some reason the latitude of the first EQ is nan when it is first printed, but prints correctly the second time.

There is quite a lot of code so I can't include it all here. The sorted_arr array is full of pointers to eq_t types.

typedef struct {
    timestamp_t* timestamp;
    float latitude;
    float longitude;
    float magnitude;
    float x;
    float y;
} eq_t;

timestamp_t is just a struct of integers (year, month, day, hour etc), and eq_print is just a wrapper around a fprintf that prints each field of eq_t and timestamp_t.

void eq_print(FILE* fp, eq_t* q)
{
    fprintf(fp, "%d-%02d-%02dT%02d:%02d:%02d.%03dZ,%.3f,%.3f,%.1f",
        q->timestamp->year,
        q->timestamp->month,
        q->timestamp->day,
        q->timestamp->hour,
        q->timestamp->min,
        q->timestamp->sec,
        q->timestamp->msec,
        q->latitude,
        q->longitude,
        q->magnitude);
}

eq_print doesn't modify the eq_t at all, so why is the printed value different when there is no code between the two fprintfs?

What could be the cause of a float having a NaN value, and then immediately after having the correct value?

Edit: Stepping through each of those lines with GDB and printing the latitude at each line prints the correct value.

Breakpoint 1, task_1_find_longest_break_after_2k_bst (
    eq_csv_file=0x28cc87 "eq_data.csv") at tasks.c:128
128             FILE* output_file = safe_open_file("task_1_bst.csv", "w");
(gdb) warning: cYgFFFFFFFF 611B75D0 0
warning: cYgstd 0x28cbdf d 3
print sorted_arr[longest_index-1]->latitude
$1 = 31.3150005
(gdb) next
129             fprintf(output_file, "timestamp,latitude,longitude,magnitude\n");
(gdb) print sorted_arr[longest_index-1]->latitude
$2 = 31.3150005
(gdb) next
130             eq_print(output_file, sorted_arr[longest_index-1]);
(gdb) print sorted_arr[longest_index-1]->latitude
$3 = 31.3150005
(gdb) next
131             fprintf(output_file, "\n");
(gdb) print sorted_arr[longest_index-1]->latitude
$4 = 31.3150005
(gdb) next
132             eq_print(output_file, sorted_arr[longest_index-1]);
(gdb) print sorted_arr[longest_index-1]->latitude
$5 = 31.3150005

There is a warning that I'm not sure how to interpret.

The eq_t q is assigned with this function

eq_t* read_quake(FILE* fp)
{
    char buf[1024];
    float latitude, longitude, magnitude;
    if (fscanf(fp, "%[^,],%f,%f,%f\n", buf, &latitude, &longitude, &magnitude) == 4) {
        eq_t* eq = (eq_t*)safe_malloc(sizeof(eq_t));
        eq->timestamp = parse_time(buf);
        eq->latitude = latitude;
        eq->longitude = longitude;
        eq->magnitude = magnitude;
        map_coordinates(eq);
        return eq;
    }
    return NULL;
}

There aren't any issues here. It seems to be a problem with the printing.

Upvotes: 4

Views: 241

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 753525

Here's a replication of your code as an MCVE (How to create a Minimal, Complete and Verifiable example).

#include <math.h>       /* NAN */
#include <stdio.h>

/*
timestamp,latitude,longitude,magnitude
2009-06-13T06:02:52.500Z,nan,-115.392,3.4
2009-06-13T06:02:52.500Z,31.315,-115.392,3.4
2009-06-13T16:04:06.650Z,3.930,126.648,4.4
*/

typedef struct timestamp_t
{
    int year;
    int month;
    int day;
    int hour;
    int min;
    int sec;
    int msec;
} timestamp_t;

typedef struct
{
    timestamp_t* timestamp;
    float latitude;
    float longitude;
    float magnitude;  // x, y unused so removed
} eq_t;

static timestamp_t times[] =
{
    { 2009, 6, 13,  6,  2, 52, 500 },
    { 2009, 6, 13, 16,  4,  6, 650 },
};

static eq_t quakes[] =
{
    { &times[0],      NAN, -115.392F, 3.4F },
    { &times[1],  +3.930F, +126.648F, 4.4F },
};

static eq_t *sorted_arr[] = { &quakes[0], &quakes[1] };

static void eq_print(FILE* fp, eq_t* q)
{
    fprintf(fp, "%d-%02d-%02dT%02d:%02d:%02d.%03dZ,%.3f,%.3f,%.1f",
            q->timestamp->year,
            q->timestamp->month,
            q->timestamp->day,
            q->timestamp->hour,
            q->timestamp->min,
            q->timestamp->sec,
            q->timestamp->msec,
            q->latitude,
            q->longitude,
            q->magnitude);
}

int main(void)
{
    int longest_index = 1;
    FILE *output_file = stdout;
    fprintf(output_file, "timestamp,latitude,longitude,magnitude\n");
    eq_print(output_file, sorted_arr[longest_index-1]);
    fprintf(output_file, "\n");
    eq_print(output_file, sorted_arr[longest_index-1]);
    fprintf(output_file, "\n");
    eq_print(output_file, sorted_arr[longest_index]);
    fprintf(output_file, "\n");
    fclose(output_file);

    return 0;
}

I called it nan11.c, and it compiles cleanly under GCC 6.1.0 on Mac OS X 10.11.6, and runs and produces the same output twice:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
     -Wold-style-definition nan11.c -o nan11  
$ ./nan11
timestamp,latitude,longitude,magnitude
2009-06-13T06:02:52.500Z,nan,-115.392,3.4
2009-06-13T06:02:52.500Z,nan,-115.392,3.4
2009-06-13T16:04:06.650Z,3.930,126.648,4.4
$

When things change unexpectedly, it normally means there's a problem with the memory management, and often means that a local variable is being returned somewhere. It's a little unexpected that the mistake 'fixes itself' on the second print operation, though.

You need to work up an MCVE analogous to what I show that actually does the reading, memory allocation, and so on, and yet still runs into problems. You should consider running that code under valgrind to see whether it can spot memory abuse.

Upvotes: 3

Related Questions