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.
This commit is contained in:
parent
cb0f634895
commit
80e145fc96
3 changed files with 51 additions and 57 deletions
|
@ -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
|
||||
|
|
7
etc/NEWS
7
etc/NEWS
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue