X-Spam-Check-By: sourceware.org Date: Fri, 16 Feb 2007 16:36:24 +0300 From: Andrew Makhorin X-Mailer: The Bat! (v2.0 Beta/1) Personal Reply-To: Andrew Makhorin Message-ID: <1433795387.20070216163624@gnu.org> To: Robin Walker CC: cygwin AT cygwin DOT com, Peter Rosin Subject: Re: strange bug in gettimeofday function In-Reply-To: References: <13811889795 DOT 20070215071733 AT gnu DOT org> <106982500 DOT 20070216041231 AT gnu DOT org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-detected-kernel: Error: This connection is not (no longer?) in the cache. X-IsSubscribed: yes Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com 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 #include #include 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/