aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2018-07-26 00:34:10 -0700
committerPaul Eggert2018-07-26 00:39:17 -0700
commit4a56ca5bbfabbb9c581828cd91648346e6b03844 (patch)
tree90b804ea4ec22a8b7be181f0b505b57c40a85c27
parent19f5f7b19b0dcdae87476a3fd51c41f840b2b80f (diff)
downloademacs-4a56ca5bbfabbb9c581828cd91648346e6b03844.tar.gz
emacs-4a56ca5bbfabbb9c581828cd91648346e6b03844.zip
%o and %x can now format signed integers
Optionally treat integers as signed numbers with %o and %x format specifiers, instead of treating them as a machine-dependent two’s complement representation. This option is more machine-independent, allows formats like "#x%x" to be useful for reading later, and is better-insulated for future changes involving bignums. Setting the new variable ‘binary-as-unsigned’ to nil enables the new behavior (Bug#32252). This is a simplified version of the change proposed in: https://lists.gnu.org/r/emacs-devel/2018-07/msg00763.html I simplified that proposal by omitting bitwidth modifiers, as I could not find an any example uses in the Emacs source code that needed them and doing them correctly would have been quite a bit more work for apparently little benefit. * doc/lispref/strings.texi (Formatting Strings): Document that %x and %o format negative integers in a platform-dependent way. Also, document how to format numbers so that the same values can be read back in. * etc/NEWS: Document the change. * src/editfns.c (styled_format): Treat integers as signed numbers even with %o and %x, if binary-as-unsigned is nil. Support the + and space flags with %o and %x, since they’re about signs. (syms_of_editfns): New variable binary-as-unsigned. * test/src/editfns-tests.el (read-large-integer): Test that maximal integers can be read after printing with all integer formats, if binary-as-unsigned is nil.
-rw-r--r--doc/lispref/strings.texi17
-rw-r--r--etc/NEWS9
-rw-r--r--src/editfns.c43
-rw-r--r--test/src/editfns-tests.el10
4 files changed, 68 insertions, 11 deletions
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index 2fff3c7c75c..3558f17301d 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -922,7 +922,8 @@ Functions}). Thus, strings are enclosed in @samp{"} characters, and
922@item %o 922@item %o
923@cindex integer to octal 923@cindex integer to octal
924Replace the specification with the base-eight representation of an 924Replace the specification with the base-eight representation of an
925unsigned integer. The object can also be a nonnegative floating-point 925integer. Negative integers are formatted in a platform-dependent
926way. The object can also be a nonnegative floating-point
926number that is formatted as an integer, dropping any fraction, if the 927number that is formatted as an integer, dropping any fraction, if the
927integer does not exceed machine limits. 928integer does not exceed machine limits.
928 929
@@ -935,7 +936,8 @@ formatted as an integer, dropping any fraction.
935@itemx %X 936@itemx %X
936@cindex integer to hexadecimal 937@cindex integer to hexadecimal
937Replace the specification with the base-sixteen representation of an 938Replace the specification with the base-sixteen representation of an
938unsigned integer. @samp{%x} uses lower case and @samp{%X} uses upper 939integer. Negative integers are formatted in a platform-dependent
940way. @samp{%x} uses lower case and @samp{%X} uses upper
939case. The object can also be a nonnegative floating-point number that 941case. The object can also be a nonnegative floating-point number that
940is formatted as an integer, dropping any fraction, if the integer does 942is formatted as an integer, dropping any fraction, if the integer does
941not exceed machine limits. 943not exceed machine limits.
@@ -1108,6 +1110,17 @@ shows only the first three characters of the representation for
1108precision is what the local library functions of the @code{printf} 1110precision is what the local library functions of the @code{printf}
1109family produce. 1111family produce.
1110 1112
1113@cindex formatting numbers for rereading later
1114 If you plan to use @code{read} later on the formatted string to
1115retrieve a copy of the formatted value, use a specification that lets
1116@code{read} reconstruct the value. To format numbers in this
1117reversible way you can use @samp{%s} and @samp{%S}, to format just
1118integers you can also use @samp{%d}, and to format just nonnegative
1119integers you can also use @samp{#x%x} and @samp{#o%o}. Other formats
1120may be problematic; for example, @samp{%d} and @samp{%g} can mishandle
1121NaNs and can lose precision and type, and @samp{#x%x} and @samp{#o%o}
1122can mishandle negative integers. @xref{Input Functions}.
1123
1111@node Case Conversion 1124@node Case Conversion
1112@section Case Conversion in Lisp 1125@section Case Conversion in Lisp
1113@cindex upper case 1126@cindex upper case
diff --git a/etc/NEWS b/etc/NEWS
index 995ceb67b78..089fc4053b1 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -812,6 +812,15 @@ between two strings.
812** 'print-quoted' now defaults to t, so if you want to see 812** 'print-quoted' now defaults to t, so if you want to see
813(quote x) instead of 'x you will have to bind it to nil where applicable. 813(quote x) instead of 'x you will have to bind it to nil where applicable.
814 814
815+++
816** Numbers formatted via %o or %x may now be formatted as signed integers.
817This avoids problems in calls like (read (format "#x%x" -1)), and is
818more compatible with bignums, a planned feature. To get this
819behavior, set the experimental variable binary-as-unsigned to nil,
820and if the new behavior breaks your code please email
82132252@debbugs.gnu.org. Because %o and %x can now format signed
822integers, they now support the + and space flags.
823
815** To avoid confusion caused by "smart quotes", the reader signals an 824** To avoid confusion caused by "smart quotes", the reader signals an
816error when reading Lisp symbols which begin with one of the following 825error when reading Lisp symbols which begin with one of the following
817quotation characters: ‘’‛“”‟〞"'. A symbol beginning with such a 826quotation characters: ‘’‛“”‟〞"'. A symbol beginning with such a
diff --git a/src/editfns.c b/src/editfns.c
index 1d6040da3f7..df257219e8f 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -4196,8 +4196,8 @@ contain either numbered or unnumbered %-sequences but not both, except
4196that %% can be mixed with numbered %-sequences. 4196that %% can be mixed with numbered %-sequences.
4197 4197
4198The + flag character inserts a + before any nonnegative number, while a 4198The + flag character inserts a + before any nonnegative number, while a
4199space inserts a space before any nonnegative number; these flags only 4199space inserts a space before any nonnegative number; these flags
4200affect %d, %e, %f, and %g sequences, and the + flag takes precedence. 4200affect only numeric %-sequences, and the + flag takes precedence.
4201The - and 0 flags affect the width specifier, as described below. 4201The - and 0 flags affect the width specifier, as described below.
4202 4202
4203The # flag means to use an alternate display form for %o, %x, %X, %e, 4203The # flag means to use an alternate display form for %o, %x, %X, %e,
@@ -4736,10 +4736,22 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4736 } 4736 }
4737 else 4737 else
4738 { 4738 {
4739 /* Don't sign-extend for octal or hex printing. */
4740 uprintmax_t x; 4739 uprintmax_t x;
4740 bool negative;
4741 if (INTEGERP (arg)) 4741 if (INTEGERP (arg))
4742 x = XUINT (arg); 4742 {
4743 if (binary_as_unsigned)
4744 {
4745 x = XUINT (arg);
4746 negative = false;
4747 }
4748 else
4749 {
4750 EMACS_INT i = XINT (arg);
4751 negative = i < 0;
4752 x = negative ? -i : i;
4753 }
4754 }
4743 else 4755 else
4744 { 4756 {
4745 double d = XFLOAT_DATA (arg); 4757 double d = XFLOAT_DATA (arg);
@@ -4747,8 +4759,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4747 if (! (0 <= d && d < uprintmax + 1)) 4759 if (! (0 <= d && d < uprintmax + 1))
4748 xsignal1 (Qoverflow_error, arg); 4760 xsignal1 (Qoverflow_error, arg);
4749 x = d; 4761 x = d;
4762 negative = false;
4750 } 4763 }
4751 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x); 4764 sprintf_buf[0] = negative ? '-' : plus_flag ? '+' : ' ';
4765 bool signedp = negative | plus_flag | space_flag;
4766 sprintf_bytes = sprintf (sprintf_buf + signedp,
4767 convspec, prec, x);
4768 sprintf_bytes += signedp;
4752 } 4769 }
4753 4770
4754 /* Now the length of the formatted item is known, except it omits 4771 /* Now the length of the formatted item is known, except it omits
@@ -5558,6 +5575,22 @@ functions if all the text being accessed has this property. */);
5558 DEFVAR_LISP ("operating-system-release", Voperating_system_release, 5575 DEFVAR_LISP ("operating-system-release", Voperating_system_release,
5559 doc: /* The release of the operating system Emacs is running on. */); 5576 doc: /* The release of the operating system Emacs is running on. */);
5560 5577
5578 DEFVAR_BOOL ("binary-as-unsigned",
5579 binary_as_unsigned,
5580 doc: /* Non-nil means `format' %x and %o treat integers as unsigned.
5581This has machine-dependent results. Nil means to treat integers as
5582signed, which is portable; for example, if N is a negative integer,
5583(read (format "#x%x") N) returns N only when this variable is nil.
5584
5585This variable is experimental; email 32252@debbugs.gnu.org if you need
5586it to be non-nil. */);
5587 /* For now, default to true if bignums exist, false in traditional Emacs. */
5588#ifdef lisp_h_FIXNUMP
5589 binary_as_unsigned = true;
5590#else
5591 binary_as_unsigned = false;
5592#endif
5593
5561 defsubr (&Spropertize); 5594 defsubr (&Spropertize);
5562 defsubr (&Schar_equal); 5595 defsubr (&Schar_equal);
5563 defsubr (&Sgoto_char); 5596 defsubr (&Sgoto_char);
diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el
index c828000bb4f..2951270dbf7 100644
--- a/test/src/editfns-tests.el
+++ b/test/src/editfns-tests.el
@@ -165,10 +165,12 @@
165 :type 'overflow-error) 165 :type 'overflow-error)
166 (should-error (read (substring (format "%d" most-negative-fixnum) 1)) 166 (should-error (read (substring (format "%d" most-negative-fixnum) 1))
167 :type 'overflow-error) 167 :type 'overflow-error)
168 (should-error (read (format "#x%x" most-negative-fixnum)) 168 (let ((binary-as-unsigned nil))
169 :type 'overflow-error) 169 (dolist (fmt '("%d" "%s" "#o%o" "#x%x"))
170 (should-error (read (format "#o%o" most-negative-fixnum)) 170 (dolist (val (list most-negative-fixnum (1+ most-negative-fixnum)
171 :type 'overflow-error) 171 -1 0 1
172 (1- most-positive-fixnum) most-positive-fixnum))
173 (should (eq val (read (format fmt val)))))))
172 (should-error (read (format "#32rG%x" most-positive-fixnum)) 174 (should-error (read (format "#32rG%x" most-positive-fixnum))
173 :type 'overflow-error)) 175 :type 'overflow-error))
174 176