(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.
This commit is contained in:
Paul Eggert 2018-06-25 12:21:40 -07:00
parent 27a21970f6
commit d0e2a341dd
5 changed files with 32 additions and 12 deletions

View file

@ -922,18 +922,23 @@ Functions}). Thus, strings are enclosed in @samp{"} characters, and
@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.

View file

@ -47,6 +47,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#ifdef HAVE_TIMEZONE_T
# include <sys/param.h>
@ -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');

View file

@ -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);
}

View file

@ -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);

View file

@ -176,6 +176,14 @@
(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