From 0cd519971d199836ba0a6e9f0e36af9b9accaf0d Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 13 Jul 2023 14:26:29 -0700 Subject: [PATCH] Port NaN, infinity handling better to VAX Nowadays .elc files routinely contain tokens like 1.0e+INF and 0.0e+NaN that do not work on antiques like the VAX that lack IEEE fp. Port Emacs to these platforms, by treating infinities as extreme values and NaNs as strings that trap if used numerically. * src/lread.c (INFINITY): Default to HUGE_VAL if non-IEEE. (not_a_number) [!IEEE_FLOATING_POINT]: New static array. (syms_of_lread) [!IEEE_FLOATING_POINT]: Initialize it. (read0): Report invalid syntax for +0.0e+NaN on platforms that lack NaNs. (string_to_number): On non-IEEE platforms, return HUGE_VAL for infinity and a string for NaN. All callers changed. --- doc/lispref/numbers.texi | 10 ++++++---- etc/NEWS | 8 ++++++++ src/data.c | 3 ++- src/lread.c | 29 ++++++++++++++++++++++++++--- src/process.c | 3 ++- 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi index 3e45aa90fda..bcf89fc9ab1 100644 --- a/doc/lispref/numbers.texi +++ b/doc/lispref/numbers.texi @@ -270,10 +270,6 @@ two NaNs as equal when their signs and significands agree. Significands of NaNs are machine-dependent, as are the digits in their string representation. - NaNs are not available on systems which do not use IEEE -floating-point arithmetic; if the read syntax for a NaN is used on a -VAX, for example, the reader signals an error. - When NaNs and signed zeros are involved, non-numeric functions like @code{eql}, @code{equal}, @code{sxhash-eql}, @code{sxhash-equal} and @code{gethash} determine whether values are indistinguishable, not @@ -283,6 +279,12 @@ whether they are numerically equal. For example, when @var{x} and conversely, @code{(equal 0.0 -0.0)} returns @code{nil} whereas @code{(= 0.0 -0.0)} returns @code{t}. + Infinities and NaNs are not available on legacy systems that lack +IEEE floating-point arithmetic. On a circa 1980 VAX, for example, the +Lisp reader approximates an infinity with the nearest finite value, +and a NaN with some other non-numeric Lisp object that provokes an +error if used numerically. + Here are read syntaxes for these special floating-point values: @table @asis diff --git a/etc/NEWS b/etc/NEWS index 5d5ea990b92..997f7e82c2b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -585,6 +585,14 @@ behavior back for any other reason, you can do that using the previous behavior of showing 'U' in the mode line for 'koi8-u': (coding-system-put 'koi8-u :mnemonic ?U) + ++++ +** Infinities and NaNs no longer act as symbols on non-IEEE platforms. +On old platforms like the VAX that do not support IEEE floating-point, +tokens like 0.0e+NaN and 1.0e+INF are no longer read as symbols. +Instead, the Lisp reader approximates an infinity with the nearest +finite value, and a NaN with some other non-numeric object that +provokes an error if used numerically. * Lisp Changes in Emacs 30.1 diff --git a/src/data.c b/src/data.c index 6de8e0cf1a1..5a31462d8ca 100644 --- a/src/data.c +++ b/src/data.c @@ -3033,7 +3033,8 @@ If the base used is not 10, STRING is always parsed as an integer. */) p++; Lisp_Object val = string_to_number (p, b, 0); - return NILP (val) ? make_fixnum (0) : val; + return ((IEEE_FLOATING_POINT ? NILP (val) : !NUMBERP (val)) + ? make_fixnum (0) : val); } enum arithop diff --git a/src/lread.c b/src/lread.c index 51d0d2a3c24..6792ef27206 100644 --- a/src/lread.c +++ b/src/lread.c @@ -75,6 +75,10 @@ along with GNU Emacs. If not, see . */ # ifndef INFINITY # define INFINITY ((union ieee754_double) {.ieee = {.exponent = -1}}.d) # endif +#else +# ifndef INFINITY +# define INFINITY HUGE_VAL +# endif #endif /* The objects or placeholders read with the #n=object form. @@ -4477,10 +4481,17 @@ substitute_in_interval (INTERVAL interval, void *arg) } +#if !IEEE_FLOATING_POINT +/* Strings that stand in for +NaN, -NaN, respectively. */ +static Lisp_Object not_a_number[2]; +#endif + /* Convert the initial prefix of STRING to a number, assuming base BASE. If the prefix has floating point syntax and BASE is 10, return a nearest float; otherwise, if the prefix has integer syntax, return - the integer; otherwise, return nil. If PLEN, set *PLEN to the + the integer; otherwise, return nil. (On antique platforms that lack + support for NaNs, if the prefix has NaN syntax return a Lisp object that + will provoke an error if used as a number.) If PLEN, set *PLEN to the length of the numeric prefix if there is one, otherwise *PLEN is unspecified. */ @@ -4545,7 +4556,6 @@ string_to_number (char const *string, int base, ptrdiff_t *plen) cp++; while ('0' <= *cp && *cp <= '9'); } -#if IEEE_FLOATING_POINT else if (cp[-1] == '+' && cp[0] == 'I' && cp[1] == 'N' && cp[2] == 'F') { @@ -4558,12 +4568,17 @@ string_to_number (char const *string, int base, ptrdiff_t *plen) { state |= E_EXP; cp += 3; +#if IEEE_FLOATING_POINT union ieee754_double u = { .ieee_nan = { .exponent = 0x7ff, .quiet_nan = 1, .mantissa0 = n >> 31 >> 1, .mantissa1 = n }}; value = u.d; - } +#else + if (plen) + *plen = cp - string; + return not_a_number[negative]; #endif + } else cp = ecp; } @@ -5707,6 +5722,14 @@ that are loaded before your customizations are read! */); DEFSYM (Qcomma, ","); DEFSYM (Qcomma_at, ",@"); +#if !IEEE_FLOATING_POINT + for (int negative = 0; negative < 2; negative++) + { + not_a_number[negative] = build_pure_c_string (&"-0.0e+NaN"[!negative]); + staticpro (¬_a_number[negative]); + } +#endif + DEFSYM (Qinhibit_file_name_operation, "inhibit-file-name-operation"); DEFSYM (Qascii_character, "ascii-character"); DEFSYM (Qfunction, "function"); diff --git a/src/process.c b/src/process.c index 67d1d3e425f..2d6e08f16b5 100644 --- a/src/process.c +++ b/src/process.c @@ -7130,7 +7130,8 @@ See function `signal-process' for more details on usage. */) { ptrdiff_t len; tem = string_to_number (SSDATA (process), 10, &len); - if (NILP (tem) || len != SBYTES (process)) + if ((IEEE_FLOATING_POINT ? NILP (tem) : !NUMBERP (tem)) + || len != SBYTES (process)) return Qnil; } process = tem; -- 2.39.2