From c990426a9883c1bd1782e6b117184b654eecda67 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Mon, 10 Sep 2012 19:28:27 -0700 Subject: [PATCH] Simplify, document, and port floating-point. The porting part of this patch fixes bugs on non-IEEE platforms with frexp, ldexp, logb. * admin/CPP-DEFINES (HAVE_CBRT, HAVE_LOGB, logb): Remove. * configure.ac (logb, cbrt): Do not check for these functions, as they are not being used. * doc/lispref/numbers.texi (Float Basics, Arithmetic Operations, Math Functions): Document that / and mod (with floating point arguments), along with asin, acos, log, log10, expt and sqrt, return special values instead of signaling exceptions. (Float Basics): Document that logb operates on the absolute value of its argument. (Math Functions): Document that (log ARG BASE) also returns NaN if BASE is negative. Document that (expt X Y) returns NaN if X is a finite negative number and Y a finite non-integer. * etc/NEWS: Document NaNs versus signaling-error change. * src/data.c, src/lisp.h (Qdomain_error, Qsingularity_error, Qunderflow_error): Now static. * src/floatfns.c: Simplify discussion of functions that Emacs doesn't support, by removing commented-out code and briefly listing the C89 functions excluded. The commented-out stuff was confusing maintenance, e.g., we thought we needed cbrt but it was commented out. (logb): Remove decl; no longer needed. (isfinite): New macro, if not already supplied. (isnan): Don't replace any existing macro. (Ffrexp, Fldexp): Define even if !HAVE_COPYSIGN, as frexp and ldexp are present on all C89 platforms. (Ffrexp): Do not special-case zero, as frexp does the right thing for that case. (Flogb): Do not use logb, as it doesn't have the desired meaning on hosts that use non-base-2 floating point. Instead, stick with frexp, which is C89 anyway. Do not pass an infinity or a NaN to frexp, to avoid getting an unspecified result. --- ChangeLog | 6 + admin/CPP-DEFINES | 4 - admin/ChangeLog | 5 + configure.ac | 6 +- doc/lispref/ChangeLog | 13 +++ doc/lispref/numbers.texi | 35 +++--- etc/ChangeLog | 5 + etc/NEWS | 8 ++ src/ChangeLog | 21 ++++ src/data.c | 4 +- src/floatfns.c | 231 ++++----------------------------------- src/lisp.h | 3 +- 12 files changed, 105 insertions(+), 236 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7d1c19a535..3d6d5020b0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2012-09-11 Paul Eggert + + Simplify, document, and port floating-point (Bug#12381). + * configure.ac (logb, cbrt): Do not check for these functions, + as they are not being used. + 2012-09-10 Paul Eggert Improve robustness of 'make bootstrap' (Bug#12376). diff --git a/admin/CPP-DEFINES b/admin/CPP-DEFINES index b40ba78e20..cf6ffee2c6 100644 --- a/admin/CPP-DEFINES +++ b/admin/CPP-DEFINES @@ -120,7 +120,6 @@ HAVE_ATTRIBUTE_ALIGNED HAVE_BDFFONT HAVE_BOXES HAVE_C99_STRTOLD -HAVE_CBRT HAVE_CFMAKERAW HAVE_CFSETSPEED HAVE_CLOCK_GETTIME @@ -251,7 +250,6 @@ HAVE_LIBXMU HAVE_LINUX_VERSION_H HAVE_LOCALTIME_R HAVE_LOCAL_SOCKETS -HAVE_LOGB HAVE_LONG_FILE_NAMES HAVE_LONG_LONG_INT HAVE_LRAND48 @@ -574,7 +572,6 @@ getpid isatty kill link -logb lseek mkdir mktemp @@ -616,7 +613,6 @@ fopen getpid index isatty -logb lseek mkdir mktemp diff --git a/admin/ChangeLog b/admin/ChangeLog index 2c61f43798..b70067d746 100644 --- a/admin/ChangeLog +++ b/admin/ChangeLog @@ -1,3 +1,8 @@ +2012-09-11 Paul Eggert + + Simplify, document, and port floating-point (Bug#12381). + * CPP-DEFINES (HAVE_CBRT, HAVE_LOGB, logb): Remove. + 2012-09-09 Paul Eggert Assume C89 or later for math functions (Bug#12381). diff --git a/configure.ac b/configure.ac index 97e967d804..9491c1da8d 100644 --- a/configure.ac +++ b/configure.ac @@ -2689,8 +2689,8 @@ if test $emacs_cv_netdb_declares_h_errno = yes; then AC_DEFINE(HAVE_H_ERRNO, 1, [Define to 1 if netdb.h declares h_errno.]) fi -# fmod, logb, and frexp are found in -lm on most systems. -# On HPUX 9.01, -lm does not contain logb, so check for sqrt. +# sqrt and other floating-point functions such as fmod and frexp +# are found in -lm on most systems. AC_CHECK_LIB(m, sqrt) # Check for mail-locking functions in a "mail" library. Probably this should @@ -2770,7 +2770,7 @@ AC_SUBST(BLESSMAIL_TARGET) AC_CHECK_FUNCS(gethostname \ closedir getrusage get_current_dir_name \ -lrand48 logb cbrt setsid \ +lrand48 setsid \ fpathconf select euidaccess getpagesize setlocale \ utimes getrlimit setrlimit setpgid getcwd shutdown getaddrinfo \ __fpending strsignal setitimer \ diff --git a/doc/lispref/ChangeLog b/doc/lispref/ChangeLog index ceb199dae8..ce99c81a91 100644 --- a/doc/lispref/ChangeLog +++ b/doc/lispref/ChangeLog @@ -1,3 +1,16 @@ +2012-09-11 Paul Eggert + + Simplify, document, and port floating-point (Bug#12381). + * numbers.texi (Float Basics, Arithmetic Operations, Math Functions): + Document that / and mod (with floating point arguments), along + with asin, acos, log, log10, expt and sqrt, return special values + instead of signaling exceptions. + (Float Basics): Document that logb operates on the absolute value + of its argument. + (Math Functions): Document that (log ARG BASE) also returns NaN if + BASE is negative. Document that (expt X Y) returns NaN if X is a + finite negative number and Y a finite non-integer. + 2012-09-09 Chong Yidong * lists.texi (Sets And Lists): Explain that the return value for diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi index 17f3ee099b..7c9672a38c 100644 --- a/doc/lispref/numbers.texi +++ b/doc/lispref/numbers.texi @@ -196,6 +196,14 @@ numerical functions return such values in cases where there is no correct answer. For example, @code{(/ 0.0 0.0)} returns a NaN. (NaN values can also carry a sign, but for practical purposes there's no significant difference between different NaN values in Emacs Lisp.) + +When a function is documented to return a NaN, it returns an +implementation-defined value when Emacs is running on one of the +now-rare platforms that do not use @acronym{IEEE} floating point. For +example, @code{(log -1.0)} typically returns a NaN, but on +non-@acronym{IEEE} platforms it returns an implementation-defined +value. + Here are the read syntaxes for these special floating point values: @table @asis @@ -241,7 +249,7 @@ numbers. @defun logb number This function returns the binary exponent of @var{number}. More -precisely, the value is the logarithm of @var{number} base 2, rounded +precisely, the value is the logarithm of |@var{number}| base 2, rounded down to an integer. @example @@ -694,7 +702,8 @@ arguments. It also permits floating point arguments; it rounds the quotient downward (towards minus infinity) to an integer, and uses that quotient to compute the remainder. -An @code{arith-error} results if @var{divisor} is 0. +If @var{divisor} is zero, @code{mod} signals an @code{arith-error} +error if both arguments are integers, and returns a NaN otherwise. @example @group @@ -1096,8 +1105,8 @@ pi/2 @tex @math{\pi/2} @end tex -(inclusive) whose sine is @var{arg}; if, however, @var{arg} is out of -range (outside [@minus{}1, 1]), it signals a @code{domain-error} error. +(inclusive) whose sine is @var{arg}. If @var{arg} is out of range +(outside [@minus{}1, 1]), @code{asin} returns a NaN. @end defun @defun acos arg @@ -1108,8 +1117,8 @@ pi @tex @math{\pi} @end tex -(inclusive) whose cosine is @var{arg}; if, however, @var{arg} is out -of range (outside [@minus{}1, 1]), it signals a @code{domain-error} error. +(inclusive) whose cosine is @var{arg}. If @var{arg} is out of range +(outside [@minus{}1, 1]), @code{acos} returns a NaN. @end defun @defun atan y &optional x @@ -1141,8 +1150,8 @@ This is the exponential function; it returns @math{e} to the power @defun log arg &optional base This function returns the logarithm of @var{arg}, with base @var{base}. If you don't specify @var{base}, the natural base -@math{e} is used. If @var{arg} is negative, it signals a -@code{domain-error} error. +@math{e} is used. If @var{arg} or @var{base} is negative, @code{log} +returns a NaN. @end defun @ignore @@ -1160,21 +1169,21 @@ lose accuracy. @end ignore @defun log10 arg -This function returns the logarithm of @var{arg}, with base 10. If -@var{arg} is negative, it signals a @code{domain-error} error. -@code{(log10 @var{x})} @equiv{} @code{(log @var{x} 10)}, at least -approximately. +This function returns the logarithm of @var{arg}, with base 10: +@code{(log10 @var{x})} @equiv{} @code{(log @var{x} 10)}. @end defun @defun expt x y This function returns @var{x} raised to power @var{y}. If both arguments are integers and @var{y} is positive, the result is an integer; in this case, overflow causes truncation, so watch out. +If @var{x} is a finite negative number and @var{y} is a finite +non-integer, @code{expt} returns a NaN. @end defun @defun sqrt arg This returns the square root of @var{arg}. If @var{arg} is negative, -it signals a @code{domain-error} error. +@code{sqrt} returns a NaN. @end defun In addition, Emacs defines the following common mathematical diff --git a/etc/ChangeLog b/etc/ChangeLog index aa78827525..11e7e5ca10 100644 --- a/etc/ChangeLog +++ b/etc/ChangeLog @@ -1,3 +1,8 @@ +2012-09-11 Paul Eggert + + Simplify, document, and port floating-point (Bug#12381). + * NEWS: Document NaNs versus signaling-error change. + 2012-09-04 Paul Eggert Give more-useful info on a fatal error (Bug#12328). diff --git a/etc/NEWS b/etc/NEWS index ef68e2e561..af4bcf11db 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -729,6 +729,14 @@ table, but with a different prefix. must be in the range 1000..9999. It now works with any year supported by the underlying C implementation. +** Floating point + +*** When floating point functions such as `log' are given invalid +arguments, e.g., (log -1.0), they now uniformly return special values +such as NaNs instead of signaling errors. Previously, these functions +returned NaNs on some platforms but signaled errors on others. The affected +functions are acos, asin, tan, exp, expt, log, log10, sqrt, and mod. + ** New function file-name-base. ** New function `tty-top-frame' returns the topmost frame of a text terminal. diff --git a/src/ChangeLog b/src/ChangeLog index 4eda17da3a..0e5bbfd252 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,26 @@ 2012-09-11 Paul Eggert + Simplify, document, and port floating-point (Bug#12381). + The porting part of this patch fixes bugs on non-IEEE platforms + with frexp, ldexp, logb. + * data.c, lisp.h (Qdomain_error, Qsingularity_error, Qunderflow_error): + Now static. + * floatfns.c: Simplify discussion of functions that Emacs doesn't + support, by removing commented-out code and briefly listing the + C89 functions excluded. The commented-out stuff was confusing + maintenance, e.g., we thought we needed cbrt but it was commented out. + (logb): Remove decl; no longer needed. + (isfinite): New macro, if not already supplied. + (isnan): Don't replace any existing macro. + (Ffrexp, Fldexp): Define even if !HAVE_COPYSIGN, as frexp and ldexp + are present on all C89 platforms. + (Ffrexp): Do not special-case zero, as frexp does the right thing + for that case. + (Flogb): Do not use logb, as it doesn't have the desired meaning + on hosts that use non-base-2 floating point. Instead, stick with + frexp, which is C89 anyway. Do not pass an infinity or a NaN to + frexp, to avoid getting an unspecified result. + * xdisp.c (Qinhibit_debug_on_message): Now static. 2012-09-10 Jan Djärv diff --git a/src/data.c b/src/data.c index a4cca0a3ee..4678ac1208 100644 --- a/src/data.c +++ b/src/data.c @@ -71,8 +71,8 @@ Lisp_Object Qchar_table_p, Qvector_or_char_table_p; Lisp_Object Qcdr; static Lisp_Object Qad_advice_info, Qad_activate_internal; -Lisp_Object Qrange_error, Qdomain_error, Qsingularity_error; -Lisp_Object Qoverflow_error, Qunderflow_error; +static Lisp_Object Qdomain_error, Qsingularity_error, Qunderflow_error; +Lisp_Object Qrange_error, Qoverflow_error; Lisp_Object Qfloatp; Lisp_Object Qnumberp, Qnumber_or_marker_p; diff --git a/src/floatfns.c b/src/floatfns.c index 8a9a9fd088..66d7ca4af2 100644 --- a/src/floatfns.c +++ b/src/floatfns.c @@ -22,9 +22,10 @@ You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ -/* C89 requires only these math.h functions: - acos, asin, atan, atan2, ceil, cos, cosh, exp, fabs, floor, fmod, - frexp, ldexp, log, log10, modf, pow, sin, sinh, sqrt, tan, tanh. +/* C89 requires only the following math.h functions, and Emacs omits + the starred functions since we haven't found a use for them: + acos, asin, atan, atan2, ceil, cos, *cosh, exp, fabs, floor, fmod, + frexp, ldexp, log, log10, *modf, pow, sin, *sinh, sqrt, tan, *tanh. */ #include @@ -42,10 +43,12 @@ along with GNU Emacs. If not, see . */ #include -/* This declaration is omitted on some systems, like Ultrix. */ -#if !defined (HPUX) && defined (HAVE_LOGB) && !defined (logb) -extern double logb (double); -#endif /* not HPUX and HAVE_LOGB and no logb macro */ +#ifndef isfinite +# define isfinite(x) ((x) - (x) == 0) +#endif +#ifndef isnan +# define isnan(x) ((x) != (x)) +#endif /* Extract a Lisp number as a `double', or signal an error. */ @@ -126,9 +129,6 @@ DEFUN ("tan", Ftan, Stan, 1, 1, 0, return make_float (d); } -#undef isnan -#define isnan(x) ((x) != (x)) - DEFUN ("isnan", Fisnan, Sisnan, 1, 1, 0, doc: /* Return non nil iff argument X is a NaN. */) (Lisp_Object x) @@ -153,6 +153,7 @@ Cause an error if X1 or X2 is not a float. */) return make_float (copysign (f1, f2)); } +#endif DEFUN ("frexp", Ffrexp, Sfrexp, 1, 1, 0, doc: /* Get significand and exponent of a floating point number. @@ -167,15 +168,9 @@ If X is zero, both parts (SGNFCAND and EXP) are zero. */) (Lisp_Object x) { double f = XFLOATINT (x); - - if (f == 0.0) - return Fcons (make_float (0.0), make_number (0)); - else - { - int exponent; - double sgnfcand = frexp (f, &exponent); - return Fcons (make_float (sgnfcand), make_number (exponent)); - } + int exponent; + double sgnfcand = frexp (f, &exponent); + return Fcons (make_float (sgnfcand), make_number (exponent)); } DEFUN ("ldexp", Fldexp, Sldexp, 1, 2, 0, @@ -187,118 +182,6 @@ Returns the floating point value resulting from multiplying SGNFCAND CHECK_NUMBER (exponent); return make_float (ldexp (XFLOATINT (sgnfcand), XINT (exponent))); } -#endif - -#if 0 /* Leave these out unless we find there's a reason for them. */ - -DEFUN ("bessel-j0", Fbessel_j0, Sbessel_j0, 1, 1, 0, - doc: /* Return the bessel function j0 of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = j0 (d); - return make_float (d); -} - -DEFUN ("bessel-j1", Fbessel_j1, Sbessel_j1, 1, 1, 0, - doc: /* Return the bessel function j1 of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = j1 (d); - return make_float (d); -} - -DEFUN ("bessel-jn", Fbessel_jn, Sbessel_jn, 2, 2, 0, - doc: /* Return the order N bessel function output jn of ARG. -The first arg (the order) is truncated to an integer. */) - (Lisp_Object n, Lisp_Object arg) -{ - int i1 = extract_float (n); - double f2 = extract_float (arg); - - f2 = jn (i1, f2); - return make_float (f2); -} - -DEFUN ("bessel-y0", Fbessel_y0, Sbessel_y0, 1, 1, 0, - doc: /* Return the bessel function y0 of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = y0 (d); - return make_float (d); -} - -DEFUN ("bessel-y1", Fbessel_y1, Sbessel_y1, 1, 1, 0, - doc: /* Return the bessel function y1 of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = y1 (d); - return make_float (d); -} - -DEFUN ("bessel-yn", Fbessel_yn, Sbessel_yn, 2, 2, 0, - doc: /* Return the order N bessel function output yn of ARG. -The first arg (the order) is truncated to an integer. */) - (Lisp_Object n, Lisp_Object arg) -{ - int i1 = extract_float (n); - double f2 = extract_float (arg); - - f2 = yn (i1, f2); - return make_float (f2); -} - -#endif - -#if 0 /* Leave these out unless we see they are worth having. */ - -DEFUN ("erf", Ferf, Serf, 1, 1, 0, - doc: /* Return the mathematical error function of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = erf (d); - return make_float (d); -} - -DEFUN ("erfc", Ferfc, Serfc, 1, 1, 0, - doc: /* Return the complementary error function of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = erfc (d); - return make_float (d); -} - -DEFUN ("log-gamma", Flog_gamma, Slog_gamma, 1, 1, 0, - doc: /* Return the log gamma of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = lgamma (d); - return make_float (d); -} - -DEFUN ("cube-root", Fcube_root, Scube_root, 1, 1, 0, - doc: /* Return the cube root of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); -#ifdef HAVE_CBRT - d = cbrt (d); -#else - if (d >= 0.0) - d = pow (d, 1.0/3.0); - else - d = -pow (-d, 1.0/3.0); -#endif - return make_float (d); -} - -#endif DEFUN ("exp", Fexp, Sexp, 1, 1, 0, doc: /* Return the exponential base e of ARG. */) @@ -383,63 +266,6 @@ DEFUN ("sqrt", Fsqrt, Ssqrt, 1, 1, 0, return make_float (d); } -#if 0 /* Not clearly worth adding. */ - -DEFUN ("acosh", Facosh, Sacosh, 1, 1, 0, - doc: /* Return the inverse hyperbolic cosine of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = acosh (d); - return make_float (d); -} - -DEFUN ("asinh", Fasinh, Sasinh, 1, 1, 0, - doc: /* Return the inverse hyperbolic sine of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = asinh (d); - return make_float (d); -} - -DEFUN ("atanh", Fatanh, Satanh, 1, 1, 0, - doc: /* Return the inverse hyperbolic tangent of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = atanh (d); - return make_float (d); -} - -DEFUN ("cosh", Fcosh, Scosh, 1, 1, 0, - doc: /* Return the hyperbolic cosine of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = cosh (d); - return make_float (d); -} - -DEFUN ("sinh", Fsinh, Ssinh, 1, 1, 0, - doc: /* Return the hyperbolic sine of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = sinh (d); - return make_float (d); -} - -DEFUN ("tanh", Ftanh, Stanh, 1, 1, 0, - doc: /* Return the hyperbolic tangent of ARG. */) - (Lisp_Object arg) -{ - double d = extract_float (arg); - d = tanh (d); - return make_float (d); -} -#endif - DEFUN ("abs", Fabs, Sabs, 1, 1, 0, doc: /* Return the absolute value of ARG. */) (register Lisp_Object arg) @@ -477,16 +303,15 @@ This is the same as the exponent of a float. */) if (f == 0.0) value = MOST_NEGATIVE_FIXNUM; - else + else if (isfinite (f)) { -#ifdef HAVE_LOGB - value = logb (f); -#else int ivalue; frexp (f, &ivalue); value = ivalue - 1; -#endif } + else + value = MOST_POSITIVE_FIXNUM; + XSETINT (val, value); return val; } @@ -719,27 +544,9 @@ syms_of_floatfns (void) defsubr (&Sisnan); #ifdef HAVE_COPYSIGN defsubr (&Scopysign); +#endif defsubr (&Sfrexp); defsubr (&Sldexp); -#endif -#if 0 - defsubr (&Sacosh); - defsubr (&Sasinh); - defsubr (&Satanh); - defsubr (&Scosh); - defsubr (&Ssinh); - defsubr (&Stanh); - defsubr (&Sbessel_y0); - defsubr (&Sbessel_y1); - defsubr (&Sbessel_yn); - defsubr (&Sbessel_j0); - defsubr (&Sbessel_j1); - defsubr (&Sbessel_jn); - defsubr (&Serf); - defsubr (&Serfc); - defsubr (&Slog_gamma); - defsubr (&Scube_root); -#endif defsubr (&Sfceiling); defsubr (&Sffloor); defsubr (&Sfround); diff --git a/src/lisp.h b/src/lisp.h index 240e8c08a8..ae9b342253 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -2559,8 +2559,7 @@ extern Lisp_Object Qchar_table_p, Qvector_or_char_table_p; extern Lisp_Object Qcdr; -extern Lisp_Object Qrange_error, Qdomain_error, Qsingularity_error; -extern Lisp_Object Qoverflow_error, Qunderflow_error; +extern Lisp_Object Qrange_error, Qoverflow_error; extern Lisp_Object Qfloatp; extern Lisp_Object Qnumberp, Qnumber_or_marker_p; -- 2.20.1