This is the mail archive of the
cygwin
mailing list for the Cygwin project.
Re: strange bug in gettimeofday function
Thank you for your response.
>> But then I would like to know why comparison of two floating-point
>> numbers leads to different results: t0 is *exactly* the same as t1,
>> nevertheless the condition t0 > t1 is true (sometimes). That is the
>> question.
>
> The multiplier 1e-6 cannot be represented exactly in binary floating
> point. Therefore the internal representation of your tv_usec will
> always be subject to rounding errors, maybe upwards or maybe
> downwards. Therefore, the result of this function will never be an
> accurate representation of the time as returned by struct timeval.
> The impact of the rounding error will depend on at what point the
> internal 80-bit value in the processor is rounded to 64 bits for
> storage as a double.
>
> If you really must do this (and it is not advised) you might do
> better by multiplying tv_sec by 1e6 then adding tv_usec unscaled, so
> that the floating point variable holds an integer number of
> microseconds.
>
> Also, if tv_sec is large, there might be significant loss of
> precision as tv_sec and tv_usec are shifted 6 decimal places (about
> 20 binary places) relative to each other in the mantissa.
Yes, that is. I just tried to reproduce a bug concerning gettimeofday,
and computing the time in such way was not my intension.
>
> Floating point representation should never be used for something for
> which you need an accurate value, or where you require to test for
> two things being equal. You have a struct which conveys the time
> accurately: why not use that? It is trivial to write functions which
> compare two timevals for equality, or yield the difference between
> two timevals.
>
The irony is that I usually give the same standard explanation about
floating-point to people who do not understand that :) *Unfortunately*,
you are right, and the difference really appears because the second
value returned by get_time being computed with full 80-bit precision
is kept in a fpu register during the comparison while the first value
is stored and then loaded as a 64-bit value. (Below here is the assembly
code explaining that.)
Nevertheless, you agree that if t0 > t1 then t0 - t1 cannot be exact
zero in *any* floating-point model, don't you? Even if optimization is
used, the compiler must not arbitrarily change the precision of the
same floating-point variable.
Andrew Makhorin
------------------------
How must not optimize:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
double get_time(void)
{ struct timeval tv;
gettimeofday(&tv, NULL);
return (double)tv.tv_sec + 1e-6 * (double)tv.tv_usec;
}
int main(void)
{ double t0 = get_time(), t1 = get_time();
if (t0 > t1)
{ int *t;
printf("sizeof(double) = %d\n", sizeof(double));
t = (int *)&t0;
printf("t0 = %08X %08X\n", t[1], t[0]);
t = (int *)&t1;
printf("t1 = %08X %08X\n", t[1], t[0]);
printf("t1 - t0 = %.20g \n", t1 - t0);
exit(EXIT_FAILURE);
}
return 0;
}
46 003e E8000000 call __alloca
46 00
47 0043 E8000000 call ___main
47 00
48 0048 E8B3FFFF call _get_time ST(0) := result
48 FF
49 004d DD5DF8 fstpl -8(%ebp) t0 := ST(0) and pop
50 0050 E8ABFFFF call _get_time ST(0) := result
50 FF
51 0055 DD45F8 fldl -8(%ebp) push t0
52 0058 D9C9 fxch %st(1) ST(0) <-> ST(1)
53 005a DD55F0 fstl -16(%ebp) t1 := ST(0)
54 005d D9C9 fxch %st(1) ST(0) <-> ST(1)
55 005f DAE9 fucompp ST(0) ? ST(1)
56 0061 DFE0 fnstsw %ax ax := status word
57 0063 9E sahf
58 0064 7704 ja L6
59 0066 C9 leave
60 0067 31C0 xorl %eax, %eax
61 0069 C3 ret
62 L6:
--
Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple
Problem reports: http://cygwin.com/problems.html
Documentation: http://cygwin.com/docs.html
FAQ: http://cygwin.com/faq/