Blame time/difftime.c

Packit 6c4009
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* Written by Paul Eggert <eggert@cs.ucla.edu>.  */
Packit 6c4009
Packit 6c4009
#include <time.h>
Packit 6c4009
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <float.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
Packit 6c4009
#define TYPE_BITS(type) (sizeof (type) * CHAR_BIT)
Packit 6c4009
#define TYPE_FLOATING(type) ((type) 0.5 == 0.5)
Packit 6c4009
#define TYPE_SIGNED(type) ((type) -1 < 0)
Packit 6c4009
Packit 6c4009
/* Return the difference between TIME1 and TIME0, where TIME0 <= TIME1.
Packit 6c4009
   time_t is known to be an integer type.  */
Packit 6c4009
Packit 6c4009
static double
Packit 6c4009
subtract (time_t time1, time_t time0)
Packit 6c4009
{
Packit 6c4009
  if (! TYPE_SIGNED (time_t))
Packit 6c4009
    return time1 - time0;
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* Optimize the common special cases where time_t
Packit 6c4009
	 can be converted to uintmax_t without losing information.  */
Packit 6c4009
      uintmax_t dt = (uintmax_t) time1 - (uintmax_t) time0;
Packit 6c4009
      double delta = dt;
Packit 6c4009
Packit 6c4009
      if (UINTMAX_MAX / 2 < INTMAX_MAX)
Packit 6c4009
	{
Packit 6c4009
	  /* This is a rare host where uintmax_t has padding bits, and possibly
Packit 6c4009
	     information was lost when converting time_t to uintmax_t.
Packit 6c4009
	     Check for overflow by comparing dt/2 to (time1/2 - time0/2).
Packit 6c4009
	     Overflow occurred if they differ by more than a small slop.
Packit 6c4009
	     Thanks to Clive D.W. Feather for detailed technical advice about
Packit 6c4009
	     hosts with padding bits.
Packit 6c4009
Packit 6c4009
	     In the following code the "h" prefix means half.  By range
Packit 6c4009
	     analysis, we have:
Packit 6c4009
Packit 6c4009
                  -0.5 <= ht1 - 0.5*time1 <= 0.5
Packit 6c4009
                  -0.5 <= ht0 - 0.5*time0 <= 0.5
Packit 6c4009
                  -1.0 <= dht - 0.5*(time1 - time0) <= 1.0
Packit 6c4009
Packit 6c4009
             If overflow has not occurred, we also have:
Packit 6c4009
Packit 6c4009
                  -0.5 <= hdt - 0.5*(time1 - time0) <= 0
Packit 6c4009
                  -1.0 <= dht - hdt <= 1.5
Packit 6c4009
Packit 6c4009
             and since dht - hdt is an integer, we also have:
Packit 6c4009
Packit 6c4009
                  -1 <= dht - hdt <= 1
Packit 6c4009
Packit 6c4009
             or equivalently:
Packit 6c4009
Packit 6c4009
                  0 <= dht - hdt + 1 <= 2
Packit 6c4009
Packit 6c4009
             In the above analysis, all the operators have their exact
Packit 6c4009
             mathematical semantics, not C semantics.  However, dht - hdt +
Packit 6c4009
             1 is unsigned in C, so it need not be compared to zero.  */
Packit 6c4009
Packit 6c4009
	  uintmax_t hdt = dt / 2;
Packit 6c4009
	  time_t ht1 = time1 / 2;
Packit 6c4009
	  time_t ht0 = time0 / 2;
Packit 6c4009
	  time_t dht = ht1 - ht0;
Packit 6c4009
Packit 6c4009
	  if (2 < dht - hdt + 1)
Packit 6c4009
	    {
Packit 6c4009
	      /* Repair delta overflow.
Packit 6c4009
Packit 6c4009
		 The following expression contains a second rounding,
Packit 6c4009
		 so the result may not be the closest to the true answer.
Packit 6c4009
		 This problem occurs only with very large differences.
Packit 6c4009
		 It's too painful to fix this portably.  */
Packit 6c4009
Packit 6c4009
	      delta = dt + 2.0L * (UINTMAX_MAX - UINTMAX_MAX / 2);
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      return delta;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return the difference between TIME1 and TIME0.  */
Packit 6c4009
double
Packit 6c4009
__difftime (time_t time1, time_t time0)
Packit 6c4009
{
Packit 6c4009
  /* Convert to double and then subtract if no double-rounding error could
Packit 6c4009
     result.  */
Packit 6c4009
Packit 6c4009
  if (TYPE_BITS (time_t) <= DBL_MANT_DIG
Packit 6c4009
      || (TYPE_FLOATING (time_t) && sizeof (time_t) < sizeof (long double)))
Packit 6c4009
    return (double) time1 - (double) time0;
Packit 6c4009
Packit 6c4009
  /* Likewise for long double.  */
Packit 6c4009
Packit 6c4009
  if (TYPE_BITS (time_t) <= LDBL_MANT_DIG || TYPE_FLOATING (time_t))
Packit 6c4009
    return (long double) time1 - (long double) time0;
Packit 6c4009
Packit 6c4009
  /* Subtract the smaller integer from the larger, convert the difference to
Packit 6c4009
     double, and then negate if needed.  */
Packit 6c4009
Packit 6c4009
  return time1 < time0 ? - subtract (time0, time1) : subtract (time1, time0);
Packit 6c4009
}
Packit 6c4009
strong_alias (__difftime, difftime)