Commit 5a955212 authored by Paul Eggert's avatar Paul Eggert

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.
parent a13c6420
Pipeline #2905 failed with stage
in 58 minutes and 22 seconds
......@@ -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:
......@@ -391,16 +391,36 @@ decode_float_time (double t, struct lisp_time *result)
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)
/* 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;
/* 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;
return FP_ILOGBNAN == INT_MAX && isnan (t) ? EINVAL : EOVERFLOW;
double scaled = scalbn (t, scale);
eassert (trunc (scaled) == scaled);
......@@ -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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment