diff options
| author | Paul Eggert | 2019-08-20 14:02:30 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-08-20 15:45:59 -0700 |
| commit | 5a9552128296478ec74594b45d0728d87450197e (patch) | |
| tree | f83d7409a2d62813ed4bb1d8b10215efefca29a3 | |
| parent | a13c64204c8ead966789abf8efe176e4f2d4f599 (diff) | |
| download | emacs-5a9552128296478ec74594b45d0728d87450197e.tar.gz emacs-5a9552128296478ec74594b45d0728d87450197e.zip | |
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.
| -rw-r--r-- | doc/lispref/os.texi | 31 | ||||
| -rw-r--r-- | src/timefns.c | 40 | ||||
| -rw-r--r-- | 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 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. | |||
| 1346 | 1346 | ||
| 1347 | @emph{Warning}: Since the result is floating point, it may not be | 1347 | @emph{Warning}: Since the result is floating point, it may not be |
| 1348 | exact. Do not use this function if precise time stamps are required. | 1348 | exact. Do not use this function if precise time stamps are required. |
| 1349 | For example, on typical systems @code{(float-time '(1 . 10))} displays | ||
| 1350 | as @samp{0.1} but is slightly greater than 1/10. | ||
| 1349 | 1351 | ||
| 1350 | @code{time-to-seconds} is an alias for this function. | 1352 | @code{time-to-seconds} is an alias for this function. |
| 1351 | @end defun | 1353 | @end defun |
| @@ -1432,8 +1434,6 @@ as traditional Gregorian years do; for example, the year number | |||
| 1432 | 1434 | ||
| 1433 | @defun time-convert time &optional form | 1435 | @defun time-convert time &optional form |
| 1434 | This function converts a time value into a Lisp timestamp. | 1436 | This function converts a time value into a Lisp timestamp. |
| 1435 | If the time cannot be represented exactly, it is truncated | ||
| 1436 | toward minus infinity. | ||
| 1437 | 1437 | ||
| 1438 | The optional @var{form} argument specifies the timestamp form to be | 1438 | The optional @var{form} argument specifies the timestamp form to be |
| 1439 | returned. If @var{form} is the symbol @code{integer}, this function | 1439 | 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 | |||
| 1452 | @code{list}, this is planned to change in a future Emacs version, so | 1452 | @code{list}, this is planned to change in a future Emacs version, so |
| 1453 | callers requiring list timestamps should pass @code{list} explicitly. | 1453 | callers requiring list timestamps should pass @code{list} explicitly. |
| 1454 | 1454 | ||
| 1455 | If @var{time} already has the proper form, this function might yield | 1455 | If @var{time} is infinite or a NaN, this function signals an error. |
| 1456 | @var{time} rather than a copy. | 1456 | Otherwise, if @var{time} cannot be represented exactly, conversion |
| 1457 | truncates it toward minus infinity. When @var{form} is @code{t}, | ||
| 1458 | conversion is always exact so no truncation occurs, and the returned | ||
| 1459 | clock resolution is no less than that of @var{time}. By way of | ||
| 1460 | contrast, @code{float-time} can convert any Lisp time value without | ||
| 1461 | signaling an error, although the result might not be exact. | ||
| 1462 | @xref{Time of Day}. | ||
| 1463 | |||
| 1464 | For efficiency this function might return a value that is @code{eq} to | ||
| 1465 | @var{time}, or that otherwise shares structure with @var{time}. | ||
| 1457 | 1466 | ||
| 1458 | Although @code{(time-convert nil nil)} is equivalent to | 1467 | Although @code{(time-convert nil nil)} is equivalent to |
| 1459 | @code{(current-time)}, the latter may be a bit faster. | 1468 | @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. | |||
| 1950 | 1959 | ||
| 1951 | @defun time-subtract t1 t2 | 1960 | @defun time-subtract t1 t2 |
| 1952 | This returns the time difference @var{t1} @minus{} @var{t2} between | 1961 | This returns the time difference @var{t1} @minus{} @var{t2} between |
| 1953 | two time values, as a time value. However, the result is a float | 1962 | two time values, normally as a Lisp timestamp but as a float |
| 1954 | if either argument is a float infinity or NaN@. | 1963 | if either argument is infinite or a NaN@. |
| 1964 | When the result is a timestamp, it is exact and its clock | ||
| 1965 | resolution is no worse than the worse of its two arguments' resolutions. | ||
| 1955 | If you need the difference in units | 1966 | If you need the difference in units |
| 1956 | of elapsed seconds, use @code{float-time} (@pxref{Time of Day, | 1967 | of elapsed seconds, you can convert it with @code{time-convert} or |
| 1957 | float-time}) to convert the result into seconds. | 1968 | @code{float-time}. @xref{Time Conversion}. |
| 1958 | @end defun | 1969 | @end defun |
| 1959 | 1970 | ||
| 1960 | @defun time-add t1 t2 | 1971 | @defun time-add t1 t2 |
| 1961 | This returns the sum of two time values, as a time value. | 1972 | This returns the sum of two time values, |
| 1962 | However, the result is a float if either argument is a float infinity or NaN@. | 1973 | using the same conversion rules as @code{time-subtract}. |
| 1963 | One argument should represent a time difference rather than a point in time, | 1974 | One argument should represent a time difference rather than a point in time, |
| 1964 | as a time value that is often just a single number of elapsed seconds. | 1975 | as a time value that is often just a single number of elapsed seconds. |
| 1965 | Here is how to add a number of seconds to a time value: | 1976 | 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) | |||
| 391 | else | 391 | else |
| 392 | { | 392 | { |
| 393 | int exponent = ilogb (t); | 393 | int exponent = ilogb (t); |
| 394 | if (exponent == FP_ILOGBNAN) | 394 | int scale; |
| 395 | return EINVAL; | 395 | if (exponent < DBL_MANT_DIG) |
| 396 | 396 | { | |
| 397 | /* An enormous or infinite T would make SCALE < 0 which would make | 397 | if (exponent < DBL_MIN_EXP - 1) |
| 398 | HZ < 1, which the (TICKS . HZ) representation does not allow. */ | 398 | { |
| 399 | if (DBL_MANT_DIG - 1 < exponent) | 399 | if (exponent == FP_ILOGBNAN |
| 400 | return EOVERFLOW; | 400 | && (FP_ILOGBNAN != FP_ILOGB0 || isnan (t))) |
| 401 | 401 | return EINVAL; | |
| 402 | /* min so we don't scale tiny numbers as if they were normalized. */ | 402 | /* T is tiny. SCALE must be less than FLT_RADIX_POWER_SIZE, |
| 403 | int scale = min (DBL_MANT_DIG - 1 - exponent, flt_radix_power_size - 1); | 403 | as otherwise T would be scaled as if it were normalized. */ |
| 404 | scale = flt_radix_power_size - 1; | ||
| 405 | } | ||
| 406 | else | ||
| 407 | { | ||
| 408 | /* The typical case. */ | ||
| 409 | scale = DBL_MANT_DIG - 1 - exponent; | ||
| 410 | } | ||
| 411 | } | ||
| 412 | else if (exponent < INT_MAX) | ||
| 413 | { | ||
| 414 | /* T is finite but so large that HZ would be less than 1 if | ||
| 415 | T's precision were represented exactly. SCALE must be | ||
| 416 | nonnegative, as the (TICKS . HZ) representation requires | ||
| 417 | HZ to be at least 1. So use SCALE = 0, which converts T to | ||
| 418 | (T . 1), which is the exact numeric value with too-large HZ, | ||
| 419 | which is typically better than signaling overflow. */ | ||
| 420 | scale = 0; | ||
| 421 | } | ||
| 422 | else | ||
| 423 | return FP_ILOGBNAN == INT_MAX && isnan (t) ? EINVAL : EOVERFLOW; | ||
| 404 | 424 | ||
| 405 | double scaled = scalbn (t, scale); | 425 | double scaled = scalbn (t, scale); |
| 406 | eassert (trunc (scaled) == scaled); | 426 | 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 @@ | |||
| 129 | most-negative-fixnum most-positive-fixnum | 129 | most-negative-fixnum most-positive-fixnum |
| 130 | (1- most-negative-fixnum) | 130 | (1- most-negative-fixnum) |
| 131 | (1+ most-positive-fixnum) | 131 | (1+ most-positive-fixnum) |
| 132 | 1e1 -1e1 1e-1 -1e-1 | ||
| 133 | 1e8 -1e8 1e-8 -1e-8 | ||
| 134 | 1e9 -1e9 1e-9 -1e-9 | ||
| 135 | 1e10 -1e10 1e-10 -1e-10 | ||
| 136 | 1e16 -1e16 1e-16 -1e-16 | ||
| 137 | 1e37 -1e37 1e-37 -1e-37 | ||
| 132 | 1e+INF -1e+INF 1e+NaN -1e+NaN | 138 | 1e+INF -1e+INF 1e+NaN -1e+NaN |
| 133 | '(0 0 0 1) '(0 0 1 0) '(0 1 0 0) '(1 0 0 0) | 139 | '(0 0 0 1) '(0 0 1 0) '(0 1 0 0) '(1 0 0 0) |
| 134 | '(-1 0 0 0) '(1 2 3 4) '(-1 2 3 4) | 140 | '(-1 0 0 0) '(1 2 3 4) '(-1 2 3 4) |