I think your assumptions are wrong.
Try these changes, and see if it dawns on you:
If it is not yet clear to you, let me spell it out:
struct ntptimeval start;
struct timeval now;
unsigned long ntp_s, ntp_us;
start.tai = 0L;
start.time.tv_sec = 0L;
start.time.tv_usec = 0L;
/* Calculate NTP time - the epoch difference is 2208988800 seconds. */
ntp_s = (unsigned long)now.tv_sec + 2208988800L;
ntp_us = (unsigned long)now.tv_usec;
printf("NTP: %.6f\n", (double)start.time.tv_sec + (double)start.time.tv_usec / 1000000.0);
printf("Now: %.6f\n", (double)now.tv_sec + (double)now.tv_usec / 1000000.0);
printf("NTP epoch: %08lx.%08lx\n", ntp_s, ntp_us);
For me, ntp_gettime() returns nonsense (actually, nanoseconds instead of microseconds in the tv_usec field, confusing the output, and causing a random difference to the actual time), but the system clock is synchronized to NTP. That is because I have the NTP daemon running, synchronizing to nearby NTP pool servers: see pool.ntp.org for example. This is exactly what you should do, too.