emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 4a56ca5: %o and %x can now format signed integers


From: Paul Eggert
Subject: [Emacs-diffs] master 4a56ca5: %o and %x can now format signed integers
Date: Thu, 26 Jul 2018 03:39:22 -0400 (EDT)

branch: master
commit 4a56ca5bbfabbb9c581828cd91648346e6b03844
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>

    %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.
---
 doc/lispref/strings.texi  | 17 +++++++++++++++--
 etc/NEWS                  |  9 +++++++++
 src/editfns.c             | 43 ++++++++++++++++++++++++++++++++++++++-----
 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 2fff3c7..3558f17 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -922,7 +922,8 @@ 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.  The object can also be a nonnegative floating-point
+integer.  Negative integers are formatted in a platform-dependent
+way.  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.
 
@@ -935,7 +936,8 @@ formatted as an integer, dropping any fraction.
 @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
+integer.  Negative integers are formatted in a platform-dependent
+way.  @samp{%x} uses lower case and @samp{%X} uses upper
 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.
@@ -1108,6 +1110,17 @@ shows only the first three characters of the 
representation for
 precision is what the local library functions of the @code{printf}
 family produce.
 
address@hidden formatting numbers for rereading later
+  If you plan to use @code{read} later on the formatted string to
+retrieve a copy of the formatted value, use a specification that lets
address@hidden reconstruct the value.  To format numbers in this
+reversible way you can use @samp{%s} and @samp{%S}, to format just
+integers you can also use @samp{%d}, and to format just nonnegative
+integers you can also use @samp{#x%x} and @samp{#o%o}.  Other formats
+may be problematic; for example, @samp{%d} and @samp{%g} can mishandle
+NaNs and can lose precision and type, and @samp{#x%x} and @samp{#o%o}
+can mishandle negative integers.  @xref{Input Functions}.
+
 @node Case Conversion
 @section Case Conversion in Lisp
 @cindex upper case
diff --git a/etc/NEWS b/etc/NEWS
index 995ceb6..089fc40 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -812,6 +812,15 @@ between two strings.
 ** 'print-quoted' now defaults to t, so if you want to see
 (quote x) instead of 'x you will have to bind it to nil where applicable.
 
++++
+** Numbers formatted via %o or %x may now be formatted as signed integers.
+This avoids problems in calls like (read (format "#x%x" -1)), and is
+more compatible with bignums, a planned feature.  To get this
+behavior, set the experimental variable binary-as-unsigned to nil,
+and if the new behavior breaks your code please email
address@hidden  Because %o and %x can now format signed
+integers, they now support the + and space flags.
+
 ** To avoid confusion caused by "smart quotes", the reader signals an
 error when reading Lisp symbols which begin with one of the following
 quotation characters: ‘’‛“”‟〞"'.  A symbol beginning with such a
diff --git a/src/editfns.c b/src/editfns.c
index 1d6040d..df25721 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -4196,8 +4196,8 @@ contain either numbered or unnumbered %-sequences but not 
both, except
 that %% can be mixed with numbered %-sequences.
 
 The + flag character inserts a + before any nonnegative number, while a
-space inserts a space before any nonnegative number; these flags only
-affect %d, %e, %f, and %g sequences, and the + flag takes precedence.
+space inserts a space before any nonnegative number; these flags
+affect only numeric %-sequences, and the + flag takes precedence.
 The - and 0 flags affect the width specifier, as described below.
 
 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)
                }
              else
                {
-                 /* Don't sign-extend for octal or hex printing.  */
                  uprintmax_t x;
+                 bool negative;
                  if (INTEGERP (arg))
-                   x = XUINT (arg);
+                   {
+                     if (binary_as_unsigned)
+                       {
+                         x = XUINT (arg);
+                         negative = false;
+                       }
+                     else
+                       {
+                         EMACS_INT i = XINT (arg);
+                         negative = i < 0;
+                         x = negative ? -i : i;
+                       }
+                   }
                  else
                    {
                      double d = XFLOAT_DATA (arg);
@@ -4747,8 +4759,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool 
message)
                      if (! (0 <= d && d < uprintmax + 1))
                        xsignal1 (Qoverflow_error, arg);
                      x = d;
+                     negative = false;
                    }
-                 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
+                 sprintf_buf[0] = negative ? '-' : plus_flag ? '+' : ' ';
+                 bool signedp = negative | plus_flag | space_flag;
+                 sprintf_bytes = sprintf (sprintf_buf + signedp,
+                                          convspec, prec, x);
+                 sprintf_bytes += signedp;
                }
 
              /* 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.  */);
   DEFVAR_LISP ("operating-system-release", Voperating_system_release,
               doc: /* The release of the operating system Emacs is running on. 
 */);
 
+  DEFVAR_BOOL ("binary-as-unsigned",
+              binary_as_unsigned,
+              doc: /* Non-nil means `format' %x and %o treat integers as 
unsigned.
+This has machine-dependent results.  Nil means to treat integers as
+signed, which is portable; for example, if N is a negative integer,
+(read (format "#x%x") N) returns N only when this variable is nil.
+
+This variable is experimental; email address@hidden if you need
+it to be non-nil.  */);
+  /* For now, default to true if bignums exist, false in traditional Emacs.  */
+#ifdef lisp_h_FIXNUMP
+  binary_as_unsigned = true;
+#else
+  binary_as_unsigned = false;
+#endif
+
   defsubr (&Spropertize);
   defsubr (&Schar_equal);
   defsubr (&Sgoto_char);
diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el
index c828000..2951270 100644
--- a/test/src/editfns-tests.el
+++ b/test/src/editfns-tests.el
@@ -165,10 +165,12 @@
                 :type 'overflow-error)
   (should-error (read (substring (format "%d" most-negative-fixnum) 1))
                 :type 'overflow-error)
-  (should-error (read (format "#x%x" most-negative-fixnum))
-                :type 'overflow-error)
-  (should-error (read (format "#o%o" most-negative-fixnum))
-                :type 'overflow-error)
+  (let ((binary-as-unsigned nil))
+    (dolist (fmt '("%d" "%s" "#o%o" "#x%x"))
+      (dolist (val (list most-negative-fixnum (1+ most-negative-fixnum)
+                         -1 0 1
+                         (1- most-positive-fixnum) most-positive-fixnum))
+        (should (eq val (read (format fmt val)))))))
   (should-error (read (format "#32rG%x" most-positive-fixnum))
                 :type 'overflow-error))
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]