From: Paul Eggert Date: Tue, 20 Aug 2019 21:02:30 +0000 (-0700) Subject: Support larger TIMEs in (time-convert TIME t) X-Git-Tag: emacs-27.0.90~1328^2~157 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=5a9552128296478ec74594b45d0728d87450197e;p=emacs.git 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. --- diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 49c07380c5f..dd80b04ad83 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 2d545a4f905..3b686eb2265 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 a30b2de3a5b..48d964d129c 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)