delorie.com/archives/browse.cgi   search  
Mail Archives: cygwin/2007/02/16/08:36:30

X-Spam-Check-By: sourceware.org
Date: Fri, 16 Feb 2007 16:36:24 +0300
From: Andrew Makhorin <mao AT gnu DOT org>
X-Mailer: The Bat! (v2.0 Beta/1) Personal
Reply-To: Andrew Makhorin <mao AT gnu DOT org>
Message-ID: <1433795387.20070216163624@gnu.org>
To: Robin Walker <rdhw AT cam DOT ac DOT uk>
CC: cygwin AT cygwin DOT com, Peter Rosin <peda AT lysator DOT liu DOT se>
Subject: Re: strange bug in gettimeofday function
In-Reply-To: <EE7C6A9E85BF2E3374078A69@qjunbur.quns.cam.ac.uk>
References: <13811889795 DOT 20070215071733 AT gnu DOT org> <er2v5b$nmj$1 AT sea DOT gmane DOT org> <106982500 DOT 20070216041231 AT gnu DOT org> <EE7C6A9E85BF2E3374078A69 AT qjunbur DOT quns DOT cam DOT ac DOT uk>
MIME-Version: 1.0
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
List-Id: <cygwin.cygwin.com>
List-Unsubscribe: <mailto:cygwin-unsubscribe-archive-cygwin=delorie DOT com AT cygwin DOT com>
List-Subscribe: <mailto:cygwin-subscribe AT cygwin DOT com>
List-Archive: <http://sourceware.org/ml/cygwin/>
List-Post: <mailto:cygwin AT cygwin DOT com>
List-Help: <mailto:cygwin-help AT cygwin DOT com>, <http://sourceware.org/ml/#faqs>
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 <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/

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019