Commit 80e145fc authored by Paul Eggert's avatar Paul Eggert
Browse files

Avoid losing info when formatting integers

* doc/lispref/numbers.texi (Integer Basics): Clarify that
out-of-range integers are treated as floating point only when the
integers are decimal.
* etc/NEWS: Mention changes.
* src/editfns.c (styled_format): Use %.0f when formatting %d or %i
values outside machine integer range, to avoid losing info.
Signal an error for %o or %x values that are too large to be
formatted, to avoid losing info.
parent cb0f6348
......@@ -53,8 +53,9 @@ but many machines provide a wider range. Many examples in this
chapter assume the minimum integer width of 30 bits.
@cindex overflow
The Lisp reader reads an integer as a sequence of digits with optional
initial sign and optional final period. An integer that is out of the
The Lisp reader reads an integer as a nonempty sequence
of decimal digits with optional initial sign and optional
final period. A decimal integer that is out of the
Emacs range is treated as a floating-point number.
@example
......
......@@ -302,6 +302,10 @@ as new-style, bind the new variable 'force-new-style-backquotes' to t.
'cl-struct-define' whose name clashes with a builtin type (e.g.,
'integer' or 'hash-table') now signals an error.
** When formatting a floating-point number as an octal or hexadecimal
integer, Emacs now signals an error if the number is too large for the
implementation to format (Bug#30408).
* Lisp Changes in Emacs 27.1
......@@ -343,6 +347,9 @@ remote systems, which support this check.
If the optional third argument is non-nil, 'make-string' will produce
a multibyte string even if its second argument is an ASCII character.
** (format "%d" X) no longer mishandles a floating-point number X that
does not fit in a machine integer (Bug#30408).
** New JSON parsing and serialization functions 'json-serialize',
'json-insert', 'json-parse-string', and 'json-parse-buffer'. These
are implemented in C using the Jansson library.
......
......@@ -4563,32 +4563,30 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
and with pM inserted for integer formats.
At most two flags F can be specified at once. */
char convspec[sizeof "%FF.*d" + max (INT_AS_LDBL, pMlen)];
{
char *f = convspec;
*f++ = '%';
/* MINUS_FLAG and ZERO_FLAG are dealt with later. */
*f = '+'; f += plus_flag;
*f = ' '; f += space_flag;
*f = '#'; f += sharp_flag;
*f++ = '.';
*f++ = '*';
if (float_conversion)
{
if (INT_AS_LDBL)
{
*f = 'L';
f += INTEGERP (arg);
}
}
else if (conversion != 'c')
{
memcpy (f, pMd, pMlen);
f += pMlen;
zero_flag &= ! precision_given;
}
*f++ = conversion;
*f = '\0';
}
char *f = convspec;
*f++ = '%';
/* MINUS_FLAG and ZERO_FLAG are dealt with later. */
*f = '+'; f += plus_flag;
*f = ' '; f += space_flag;
*f = '#'; f += sharp_flag;
*f++ = '.';
*f++ = '*';
if (float_conversion)
{
if (INT_AS_LDBL)
{
*f = 'L';
f += INTEGERP (arg);
}
}
else if (conversion != 'c')
{
memcpy (f, pMd, pMlen);
f += pMlen;
zero_flag &= ! precision_given;
}
*f++ = conversion;
*f = '\0';
int prec = -1;
if (precision_given)
......@@ -4630,29 +4628,20 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
}
else if (conversion == 'd' || conversion == 'i')
{
/* For float, maybe we should use "%1.0f"
instead so it also works for values outside
the integer range. */
printmax_t x;
if (INTEGERP (arg))
x = XINT (arg);
{
printmax_t x = XINT (arg);
sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
}
else
{
double d = XFLOAT_DATA (arg);
if (d < 0)
{
x = TYPE_MINIMUM (printmax_t);
if (x < d)
x = d;
}
else
{
x = TYPE_MAXIMUM (printmax_t);
if (d < x)
x = d;
}
strcpy (f - pMlen - 1, "f");
double x = XFLOAT_DATA (arg);
sprintf_bytes = sprintf (sprintf_buf, convspec, 0, x);
char c0 = sprintf_buf[0];
bool signedp = ! ('0' <= c0 && c0 <= '9');
prec = min (precision, sprintf_bytes - signedp);
}
sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
}
else
{
......@@ -4663,22 +4652,19 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
else
{
double d = XFLOAT_DATA (arg);
if (d < 0)
x = 0;
else
{
x = TYPE_MAXIMUM (uprintmax_t);
if (d < x)
x = d;
}
double uprintmax = TYPE_MAXIMUM (uprintmax_t);
if (! (0 <= d && d < uprintmax + 1))
xsignal1 (Qoverflow_error, arg);
x = d;
}
sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
}
/* Now the length of the formatted item is known, except it omits
padding and excess precision. Deal with excess precision
first. This happens only when the format specifies
ridiculously large precision. */
first. This happens when the format specifies ridiculously
large precision, or when %d or %i formats a float that would
ordinarily need fewer digits than a specified precision. */
ptrdiff_t excess_precision
= precision_given ? precision - prec : 0;
ptrdiff_t leading_zeros = 0, trailing_zeros = 0;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment