diff options
| author | Paul Eggert | 2018-07-26 00:34:10 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-07-26 00:39:17 -0700 |
| commit | 4a56ca5bbfabbb9c581828cd91648346e6b03844 (patch) | |
| tree | 90b804ea4ec22a8b7be181f0b505b57c40a85c27 | |
| parent | 19f5f7b19b0dcdae87476a3fd51c41f840b2b80f (diff) | |
| download | emacs-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.texi | 17 | ||||
| -rw-r--r-- | etc/NEWS | 9 | ||||
| -rw-r--r-- | src/editfns.c | 43 | ||||
| -rw-r--r-- | test/src/editfns-tests.el | 10 |
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 |
| 924 | Replace the specification with the base-eight representation of an | 924 | Replace the specification with the base-eight representation of an |
| 925 | unsigned integer. The object can also be a nonnegative floating-point | 925 | integer. Negative integers are formatted in a platform-dependent |
| 926 | way. The object can also be a nonnegative floating-point | ||
| 926 | number that is formatted as an integer, dropping any fraction, if the | 927 | number that is formatted as an integer, dropping any fraction, if the |
| 927 | integer does not exceed machine limits. | 928 | integer 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 |
| 937 | Replace the specification with the base-sixteen representation of an | 938 | Replace the specification with the base-sixteen representation of an |
| 938 | unsigned integer. @samp{%x} uses lower case and @samp{%X} uses upper | 939 | integer. Negative integers are formatted in a platform-dependent |
| 940 | way. @samp{%x} uses lower case and @samp{%X} uses upper | ||
| 939 | case. The object can also be a nonnegative floating-point number that | 941 | case. The object can also be a nonnegative floating-point number that |
| 940 | is formatted as an integer, dropping any fraction, if the integer does | 942 | is formatted as an integer, dropping any fraction, if the integer does |
| 941 | not exceed machine limits. | 943 | not exceed machine limits. |
| @@ -1108,6 +1110,17 @@ shows only the first three characters of the representation for | |||
| 1108 | precision is what the local library functions of the @code{printf} | 1110 | precision is what the local library functions of the @code{printf} |
| 1109 | family produce. | 1111 | family produce. |
| 1110 | 1112 | ||
| 1113 | @cindex formatting numbers for rereading later | ||
| 1114 | If you plan to use @code{read} later on the formatted string to | ||
| 1115 | retrieve a copy of the formatted value, use a specification that lets | ||
| 1116 | @code{read} reconstruct the value. To format numbers in this | ||
| 1117 | reversible way you can use @samp{%s} and @samp{%S}, to format just | ||
| 1118 | integers you can also use @samp{%d}, and to format just nonnegative | ||
| 1119 | integers you can also use @samp{#x%x} and @samp{#o%o}. Other formats | ||
| 1120 | may be problematic; for example, @samp{%d} and @samp{%g} can mishandle | ||
| 1121 | NaNs and can lose precision and type, and @samp{#x%x} and @samp{#o%o} | ||
| 1122 | can 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 |
| @@ -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. | ||
| 817 | This avoids problems in calls like (read (format "#x%x" -1)), and is | ||
| 818 | more compatible with bignums, a planned feature. To get this | ||
| 819 | behavior, set the experimental variable binary-as-unsigned to nil, | ||
| 820 | and if the new behavior breaks your code please email | ||
| 821 | 32252@debbugs.gnu.org. Because %o and %x can now format signed | ||
| 822 | integers, 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 |
| 816 | error when reading Lisp symbols which begin with one of the following | 825 | error when reading Lisp symbols which begin with one of the following |
| 817 | quotation characters: ‘’‛“”‟〞"'. A symbol beginning with such a | 826 | quotation 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 | |||
| 4196 | that %% can be mixed with numbered %-sequences. | 4196 | that %% can be mixed with numbered %-sequences. |
| 4197 | 4197 | ||
| 4198 | The + flag character inserts a + before any nonnegative number, while a | 4198 | The + flag character inserts a + before any nonnegative number, while a |
| 4199 | space inserts a space before any nonnegative number; these flags only | 4199 | space inserts a space before any nonnegative number; these flags |
| 4200 | affect %d, %e, %f, and %g sequences, and the + flag takes precedence. | 4200 | affect only numeric %-sequences, and the + flag takes precedence. |
| 4201 | The - and 0 flags affect the width specifier, as described below. | 4201 | The - and 0 flags affect the width specifier, as described below. |
| 4202 | 4202 | ||
| 4203 | The # flag means to use an alternate display form for %o, %x, %X, %e, | 4203 | The # 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. | ||
| 5581 | This has machine-dependent results. Nil means to treat integers as | ||
| 5582 | signed, 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 | |||
| 5585 | This variable is experimental; email 32252@debbugs.gnu.org if you need | ||
| 5586 | it 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 | ||