>From 32a9df02ac3c5b12d7ff6efa0c3924d740d70268 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Mon, 25 Jun 2018 12:21:40 -0700 Subject: [PATCH] (format "%d" F) now truncates floating F Problem reported by Paul Pogonyshev (Bug#31938). * src/editfns.c: Include math.h, for trunc. (styled_format): For %d, truncate floating-point numbers and convert -0 to 0, going back to how Emacs 26 did things. * doc/lispref/strings.texi (Formatting Strings): Document behavior of %o, %d, %x, %X on floating-point numbers. * src/floatfns.c (trunc) [!HAVE_TRUNC]: Rename from emacs_trunc and make it an extern function, so that editfns.c can use it. All callers changed. * test/src/editfns-tests.el (format-%d-float): New test. --- doc/lispref/strings.texi | 11 ++++++++--- src/editfns.c | 7 +++++++ src/floatfns.c | 13 +++++-------- src/lisp.h | 5 ++++- test/src/editfns-tests.el | 8 ++++++++ 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi index 70ba1aa613..026ba749cb 100644 --- a/doc/lispref/strings.texi +++ b/doc/lispref/strings.texi @@ -922,18 +922,23 @@ Formatting Strings @item %o @cindex integer to octal Replace the specification with the base-eight representation of an -unsigned integer. +unsigned integer. The object can also be a nonnegative floating-point +number that is formatted as an integer, dropping any fraction, if the +integer does not exceed machine limits. @item %d Replace the specification with the base-ten representation of a signed -integer. +integer. The object can also be a floating-point number that is +formatted as an integer, dropping any fraction. @item %x @itemx %X @cindex integer to hexadecimal Replace the specification with the base-sixteen representation of an unsigned integer. @samp{%x} uses lower case and @samp{%X} uses upper -case. +case. The object can also be a nonnegative floating-point number that +is formatted as an integer, dropping any fraction, if the integer does +not exceed machine limits. @item %c Replace the specification with the character which is the value given. diff --git a/src/editfns.c b/src/editfns.c index 30d585cd01..7d032a7ca4 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -47,6 +47,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #ifdef HAVE_TIMEZONE_T # include @@ -4671,6 +4672,12 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) { strcpy (f - pMlen - 1, "f"); double x = XFLOAT_DATA (arg); + + /* Truncate and then convert -0 to 0, to be more + consistent with %x etc.; see Bug#31938. */ + x = trunc (x); + x = x ? x : 0; + sprintf_bytes = sprintf (sprintf_buf, convspec, 0, x); char c0 = sprintf_buf[0]; bool signedp = ! ('0' <= c0 && c0 <= '9'); diff --git a/src/floatfns.c b/src/floatfns.c index ec0349fbf4..e7d404a84e 100644 --- a/src/floatfns.c +++ b/src/floatfns.c @@ -435,11 +435,9 @@ emacs_rint (double d) } #endif -#ifdef HAVE_TRUNC -#define emacs_trunc trunc -#else -static double -emacs_trunc (double d) +#ifndef HAVE_TRUNC +double +trunc (double d) { return (d < 0 ? ceil : floor) (d); } @@ -482,8 +480,7 @@ Rounds ARG toward zero. With optional DIVISOR, truncate ARG/DIVISOR. */) (Lisp_Object arg, Lisp_Object divisor) { - return rounding_driver (arg, divisor, emacs_trunc, truncate2, - "truncate"); + return rounding_driver (arg, divisor, trunc, truncate2, "truncate"); } @@ -543,7 +540,7 @@ DEFUN ("ftruncate", Fftruncate, Sftruncate, 1, 1, 0, { CHECK_FLOAT (arg); double d = XFLOAT_DATA (arg); - d = emacs_trunc (d); + d = trunc (d); return make_float (d); } diff --git a/src/lisp.h b/src/lisp.h index d0c52d8567..8c884dce15 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3425,8 +3425,11 @@ extern Lisp_Object string_make_unibyte (Lisp_Object); extern void syms_of_fns (void); /* Defined in floatfns.c. */ -extern void syms_of_floatfns (void); +#ifndef HAVE_TRUNC +extern double trunc (double); +#endif extern Lisp_Object fmod_float (Lisp_Object x, Lisp_Object y); +extern void syms_of_floatfns (void); /* Defined in fringe.c. */ extern void syms_of_fringe (void); diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el index 1ed0bd5bba..c828000bb4 100644 --- a/test/src/editfns-tests.el +++ b/test/src/editfns-tests.el @@ -176,6 +176,14 @@ transpose-test-get-byte-positions (should-error (format "%o" -1e-37) :type 'overflow-error)) +;; Bug#31938 +(ert-deftest format-%d-float () + (should (string-equal (format "%d" -1.1) "-1")) + (should (string-equal (format "%d" -0.9) "0")) + (should (string-equal (format "%d" -0.0) "0")) + (should (string-equal (format "%d" 0.0) "0")) + (should (string-equal (format "%d" 0.9) "0")) + (should (string-equal (format "%d" 1.1) "1"))) ;;; Check format-time-string with various TZ settings. ;;; Use only POSIX-compatible TZ values, since the tests should work -- 2.17.1