emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 5a95521: Support larger TIMEs in (time-convert TIME


From: Paul Eggert
Subject: [Emacs-diffs] master 5a95521: Support larger TIMEs in (time-convert TIME t)
Date: Tue, 20 Aug 2019 18:46:04 -0400 (EDT)

branch: master
commit 5a9552128296478ec74594b45d0728d87450197e
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>

    Support larger TIMEs in (time-convert TIME t)
    
    Also, improve the doc to match current behavior.
    * doc/lispref/os.texi (Time Conversion): Document that
    time-convert signals an error for infinite or NaN args,
    and that (time-convert TIME t) is exact otherwise.
    Mention float-time as an alternative to time-convert.
    (Time Calculations): Document that time-add and time-subtract
    are exact and do not decrease HZ below the minimum of their args.
    * src/timefns.c (decode_float_time): Don’t signal an error for
    floating-point arguments whose base-FLT_RADIX exponent is not less
    than DBL_MANT_DIG.  Instead, convert them to (TICKS . 1) values.
    Use two (instead of three) integer exponent comparisons in the
    typical case.
    * test/src/timefns-tests.el (time-arith-tests):
    Add more floating-point tests, including some tests
    that the old code fails.
---
 doc/lispref/os.texi       | 31 +++++++++++++++++++++----------
 src/timefns.c             | 40 ++++++++++++++++++++++++++++++----------
 test/src/timefns-tests.el |  6 ++++++
 3 files changed, 57 insertions(+), 20 deletions(-)

diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 49c0738..dd80b04 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -1346,6 +1346,8 @@ given, specifies a time to convert instead of the current 
time.
 
 @emph{Warning}: Since the result is floating point, it may not be
 exact.  Do not use this function if precise time stamps are required.
+For example, on typical systems @code{(float-time '(1 . 10))} displays
+as @samp{0.1} but is slightly greater than 1/10.
 
 @code{time-to-seconds} is an alias for this function.
 @end defun
@@ -1432,8 +1434,6 @@ as traditional Gregorian years do; for example, the year 
number
 
 @defun time-convert time &optional form
 This function converts a time value into a Lisp timestamp.
-If the time cannot be represented exactly, it is truncated
-toward minus infinity.
 
 The optional @var{form} argument specifies the timestamp form to be
 returned.  If @var{form} is the symbol @code{integer}, this function
@@ -1452,8 +1452,17 @@ Although an omitted or @code{nil} @var{form} currently 
acts like
 @code{list}, this is planned to change in a future Emacs version, so
 callers requiring list timestamps should pass @code{list} explicitly.
 
-If @var{time} already has the proper form, this function might yield
-@var{time} rather than a copy.
+If @var{time} is infinite or a NaN, this function signals an error.
+Otherwise, if @var{time} cannot be represented exactly, conversion
+truncates it toward minus infinity.  When @var{form} is @code{t},
+conversion is always exact so no truncation occurs, and the returned
+clock resolution is no less than that of @var{time}.  By way of
+contrast, @code{float-time} can convert any Lisp time value without
+signaling an error, although the result might not be exact.
+@xref{Time of Day}.
+
+For efficiency this function might return a value that is @code{eq} to
+@var{time}, or that otherwise shares structure with @var{time}.
 
 Although @code{(time-convert nil nil)} is equivalent to
 @code{(current-time)}, the latter may be a bit faster.
@@ -1950,16 +1959,18 @@ The result is @code{nil} if either argument is a NaN.
 
 @defun time-subtract t1 t2
 This returns the time difference @var{t1} @minus{} @var{t2} between
-two time values, as a time value.  However, the result is a float
-if either argument is a float infinity or NaN@.
+two time values, normally as a Lisp timestamp but as a float
+if either argument is infinite or a NaN@.
+When the result is a timestamp, it is exact and its clock
+resolution is no worse than the worse of its two arguments' resolutions.
 If you need the difference in units
-of elapsed seconds, use @code{float-time} (@pxref{Time of Day,
-float-time}) to convert the result into seconds.
+of elapsed seconds, you can convert it with @code{time-convert} or
+@code{float-time}.  @xref{Time Conversion}.
 @end defun
 
 @defun time-add t1 t2
-This returns the sum of two time values, as a time value.
-However, the result is a float if either argument is a float infinity or NaN@.
+This returns the sum of two time values,
+using the same conversion rules as @code{time-subtract}.
 One argument should represent a time difference rather than a point in time,
 as a time value that is often just a single number of elapsed seconds.
 Here is how to add a number of seconds to a time value:
diff --git a/src/timefns.c b/src/timefns.c
index 2d545a4..3b686eb 100644
--- a/src/timefns.c
+++ b/src/timefns.c
@@ -391,16 +391,36 @@ decode_float_time (double t, struct lisp_time *result)
   else
     {
       int exponent = ilogb (t);
-      if (exponent == FP_ILOGBNAN)
-       return EINVAL;
-
-      /* An enormous or infinite T would make SCALE < 0 which would make
-        HZ < 1, which the (TICKS . HZ) representation does not allow.  */
-      if (DBL_MANT_DIG - 1 < exponent)
-       return EOVERFLOW;
-
-      /* min so we don't scale tiny numbers as if they were normalized.  */
-      int scale = min (DBL_MANT_DIG - 1 - exponent, flt_radix_power_size - 1);
+      int scale;
+      if (exponent < DBL_MANT_DIG)
+       {
+         if (exponent < DBL_MIN_EXP - 1)
+           {
+             if (exponent == FP_ILOGBNAN
+                 && (FP_ILOGBNAN != FP_ILOGB0 || isnan (t)))
+               return EINVAL;
+             /* T is tiny.  SCALE must be less than FLT_RADIX_POWER_SIZE,
+                as otherwise T would be scaled as if it were normalized.  */
+             scale = flt_radix_power_size - 1;
+           }
+         else
+           {
+             /* The typical case.  */
+             scale = DBL_MANT_DIG - 1 - exponent;
+           }
+       }
+      else if (exponent < INT_MAX)
+       {
+        /* T is finite but so large that HZ would be less than 1 if
+           T's precision were represented exactly.  SCALE must be
+           nonnegative, as the (TICKS . HZ) representation requires
+           HZ to be at least 1.  So use SCALE = 0, which converts T to
+           (T . 1), which is the exact numeric value with too-large HZ,
+           which is typically better than signaling overflow.  */
+         scale = 0;
+       }
+      else
+       return FP_ILOGBNAN == INT_MAX && isnan (t) ? EINVAL : EOVERFLOW;
 
       double scaled = scalbn (t, scale);
       eassert (trunc (scaled) == scaled);
diff --git a/test/src/timefns-tests.el b/test/src/timefns-tests.el
index a30b2de..48d964d 100644
--- a/test/src/timefns-tests.el
+++ b/test/src/timefns-tests.el
@@ -129,6 +129,12 @@
                           most-negative-fixnum most-positive-fixnum
                           (1- most-negative-fixnum)
                           (1+ most-positive-fixnum)
+                          1e1 -1e1 1e-1 -1e-1
+                          1e8 -1e8 1e-8 -1e-8
+                          1e9 -1e9 1e-9 -1e-9
+                          1e10 -1e10 1e-10 -1e-10
+                          1e16 -1e16 1e-16 -1e-16
+                          1e37 -1e37 1e-37 -1e-37
                           1e+INF -1e+INF 1e+NaN -1e+NaN
                           '(0 0 0 1) '(0 0 1 0) '(0 1 0 0) '(1 0 0 0)
                           '(-1 0 0 0) '(1 2 3 4) '(-1 2 3 4)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]