Subject: bug#60583: Support for new SI prefixes R, Q

From: Paul Eggert <[email-masked]>
Date: Thu, 5 Jan 2023 13:11:46 -0800

Building on Pádraig's Gnulib patches of November, I installed the 
attached to add support to Coreutils for the new SI prefixes R and Q.

It'll be a while before 'df -h' outputs them, as I'm not expecting file 
systems in the ronnabyte range soon, given that the total amount of data 
storage in the world is still less than a yottabyte, and given that 
we'll need 128-bit integers support all this consistently. Still, the 
new R and Q prefixes are supported by 'sort' and 'numfmt' as-is.

From b7c8754c90a957ef0fa5858cae372be26da69135 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Thu, 5 Jan 2023 11:42:51 -0800
Subject: [PATCH 1/3] build: update gnulib submodule to latest

---
 gnulib | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gnulib b/gnulib
index 875461ffd..6f6ca19e6 160000
--- a/gnulib
+++ b/gnulib
@@ -1 +1 @@
-Subproject commit 875461ffdf58ac04677957b4ae4160465b83b940
+Subproject commit 6f6ca19e6cec9fec055258b1459108ef8822fe75
-- 
2.37.2

From 26bac321370acba2f651349dcc14beb37474a396 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Thu, 5 Jan 2023 11:42:51 -0800
Subject: [PATCH 2/3] ls: adjust to Gnulib renaming

* src/local.mk (src_ls_LDADD): In Gnulib,
LIB_HAS_ACL was renamed to FILE_HAS_ACL_LIB.
---
 src/local.mk | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/local.mk b/src/local.mk
index 1faf50e57..3f664ed1a 100644
--- a/src/local.mk
+++ b/src/local.mk
@@ -288,7 +288,7 @@ src_uptime_LDADD += $(GETLOADAVG_LIBS)
 
 # for various ACL functions
 copy_ldadd += $(LIB_ACL)
-src_ls_LDADD += $(LIB_HAS_ACL)
+src_ls_LDADD += $(FILE_HAS_ACL_LIB)
 
 # for various xattr functions
 copy_ldadd += $(LIB_XATTR)
-- 
2.37.2

From e1ff4ee0f6d7490610ecc39d57c855818f6fd1f4 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Thu, 5 Jan 2023 11:42:51 -0800
Subject: [PATCH 3/3] numfmt: add support for new SI prefixes

* src/dd, src/head.c, src/od.c, src/sort.c, src/stdbuf.c, src/tail.c:
(usage):
* src/system.h (emit_size_note):
Mention new SI prefixes.
* src/du.c (main):
* src/head.c (head_file):
* src/numfmt.c (suffix_power, suffix_power_char, prepare_padded_number):
* src/shred.c (main):
* src/sort.c (unit_order):
* src/tail.c (parse_options):
Support new SI prefixes.
* src/numfmt.c (MAX_ACCEPTABLE_DIGITS): Increase to 33.
(zero_and_valid_suffixes, valid_suffixes): New constants,
with new SI prefixes.
(valid_suffix, unit_to_umax): Use them.
(prepare_padded_number): Diagnose "999Q" instead of "999Y".
* tests/misc/numfmt.pl, tests/misc/sort.pl:
Adjust tests to match new max.
---
 NEWS                 |  6 ++++
 doc/coreutils.texi   | 41 ++++++++++++++++++------
 src/dd.c             |  2 +-
 src/du.c             |  2 +-
 src/head.c           |  4 +--
 src/numfmt.c         | 38 +++++++++++++++-------
 src/od.c             |  2 +-
 src/shred.c          |  2 +-
 src/sort.c           | 14 ++++----
 src/stdbuf.c         |  2 +-
 src/system.h         |  2 +-
 src/tail.c           |  4 +--
 tests/misc/numfmt.pl | 76 ++++++++++++++++++++++++--------------------
 tests/misc/sort.pl   |  8 ++---
 14 files changed, 126 insertions(+), 77 deletions(-)

diff --git a/NEWS b/NEWS
index 04ee933c0..52c92ddb4 100644
--- a/NEWS
+++ b/NEWS
@@ -48,6 +48,12 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** Changes in behavior
 
+  Programs now use the new SI prefixes R (10**27) and Q (10**30)
+  along with their binary counterparts Ri (2**90) and Qi (2**100).
+  In some cases (e.g., 'sort -h') these new prefixes simply work;
+  in others, where they exceed integer width limits, they now elicit
+  the same integer overflow diagnostics as other large prefixes.
+
   'cp --reflink=always A B' no longer leaves behind a newly created
   empty file B merely because copy-on-write clones are not supported.
 
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index bd9e00de4..4c278bf27 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -707,7 +707,8 @@ one of the following multiplicative suffixes:
 @samp{GB} => 1000*1000*1000 (GigaBytes)
 @samp{G}  => 1024*1024*1024 (GibiBytes)
 @end example
-and so on for @samp{T}, @samp{P}, @samp{E}, @samp{Z}, and @samp{Y}.
+and so on for @samp{T}, @samp{P}, @samp{E}, @samp{Z}, @samp{Y},
+@samp{R}, and @samp{Q}.
 Binary prefixes can be used, too: @samp{KiB}=@samp{K}, @samp{MiB}=@samp{M},
 and so on.
 @end macro
@@ -724,7 +725,8 @@ one of the following multiplicative suffixes:
 @samp{GB} => 1000*1000*1000 (GigaBytes)
 @samp{G}  => 1024*1024*1024 (GibiBytes)
 @end example
-and so on for @samp{T}, @samp{P}, @samp{E}, @samp{Z}, and @samp{Y}.
+and so on for @samp{T}, @samp{P}, @samp{E}, @samp{Z}, @samp{Y},
+@samp{R}, and @samp{Q}.
 Binary prefixes can be used, too: @samp{KiB}=@samp{K}, @samp{MiB}=@samp{M},
 and so on.
 @end macro
@@ -982,7 +984,7 @@ prepended, except that it causes a size indication to be appended to
 the output.  For example, @samp{--block-size="kB"} displays 3000 as
 @samp{3kB}.
 
-The following suffixes are defined.  Large sizes like @code{1Y}
+The following suffixes are defined.  Large sizes like @code{1Q}
 may be rejected by your computer due to limitations of its arithmetic.
 
 @table @samp
@@ -1036,13 +1038,25 @@ exbibyte: @math{2^{60} = 1,152,921,504,606,846,976}.
 zettabyte: @math{10^{21} = 1,000,000,000,000,000,000,000}
 @item Z
 @itemx ZiB
-@math{2^{70} = 1,180,591,620,717,411,303,424}.
+zebibyte: @math{2^{70} = 1,180,591,620,717,411,303,424}.
 @item YB
 @cindex yottabyte, definition of
 yottabyte: @math{10^{24} = 1,000,000,000,000,000,000,000,000}.
 @item Y
 @itemx YiB
-@math{2^{80} = 1,208,925,819,614,629,174,706,176}.
+yobibyte: @math{2^{80} = 1,208,925,819,614,629,174,706,176}.
+@item RB
+@cindex ronnabyte, definition of
+ronnabyte: @math{10^{27} = 1,000,000,000,000,000,000,000,000,000}.
+@item R
+@itemx RiB
+robibyte: @math{2^{90} = 1,237,940,039,285,380,274,899,124,224}.
+@item QB
+@cindex quettabyte, definition of
+quettabyte: @math{10^{30} = 1,000,000,000,000,000,000,000,000,000,000}.
+@item Q
+@itemx QiB
+quebibyte: @math{2^{100} = 1,267,650,600,228,229,401,496,703,205,376}.
 @end table
 
 @opindex -k
@@ -4522,7 +4536,7 @@ lexicographic sort will be faster.
 @vindex LC_NUMERIC
 Sort numerically, first by numeric sign (negative, zero, or positive);
 then by SI suffix (either empty, or @samp{k} or @samp{K}, or
-one of @samp{MGTPEZY}, in that order; @pxref{Block size}); and finally
+one of @samp{MGTPEZYRQ}, in that order; @pxref{Block size}); and finally
 by numeric value.  For example, @samp{1023M} sorts before @samp{1G}
 because @samp{M} (mega) precedes @samp{G} (giga) as an SI
 suffix.  This option sorts values that are consistently scaled to the
@@ -4759,7 +4773,8 @@ Use a main-memory sort buffer of the given @var{size}.  By default,
 @var{size} to be interpreted as a percentage of physical memory.
 Appending @samp{K} multiplies @var{size} by 1024 (the default),
 @samp{M} by 1,048,576, @samp{G} by 1,073,741,824, and so on for
-@samp{T}, @samp{P}, @samp{E}, @samp{Z}, and @samp{Y}@.  Appending
+@samp{T}, @samp{P}, @samp{E}, @samp{Z}, @samp{Y}, @samp{R}, and @samp{Q}@.
+Appending
 @samp{b} causes @var{size} to be interpreted as a byte count, with no
 multiplication.
 
@@ -18961,6 +18976,8 @@ one of the following suffixes:
 @samp{E}  =>  @math{1000^6 = 10^{18}} (Exa)
 @samp{Z}  =>  @math{1000^7 = 10^{21}} (Zetta)
 @samp{Y}  =>  @math{1000^8 = 10^{24}} (Yotta)
+@samp{R}  =>  @math{1000^9 = 10^{27}} (Ronna)
+@samp{Q}  =>  @math{1000^{10} = 10^{30}} (Quetta)
 @end example
 
 @item iec
@@ -18979,6 +18996,8 @@ one of the following suffixes:
 @samp{E}  =>  @math{1024^6 = 2^{60}} (Exbi)
 @samp{Z}  =>  @math{1024^7 = 2^{70}} (Zebi)
 @samp{Y}  =>  @math{1024^8 = 2^{80}} (Yobi)
+@samp{R}  =>  @math{1024^9 = 2^{90}} (Robi)
+@samp{Q}  =>  @math{1024^{10} = 2^{100}} (Quebi)
 @end example
 
 The @option{iec} option uses a single letter suffix (e.g. @samp{G}), which is
@@ -19002,6 +19021,8 @@ one of the following suffixes:
 @samp{Ei}  =>  @math{1024^6 = 2^{60}} (Exbi)
 @samp{Zi}  =>  @math{1024^7 = 2^{70}} (Zebi)
 @samp{Yi}  =>  @math{1024^8 = 2^{80}} (Yobi)
+@samp{Ri}  =>  @math{1024^9 = 2^{90}} (Robi)
+@samp{Qi}  =>  @math{1024^{10} = 2^{100}} (Quebi)
 @end example
 
 The @option{iec-i} option uses a two-letter suffix symbol (e.g. @samp{Gi}),
@@ -19010,9 +19031,9 @@ practice.  Compare with the @option{iec} option.
 
 @item auto
 @samp{auto} can only be used with @option{--from}.  With this method, numbers
-with @samp{K},@samp{M},@samp{G},@samp{T},@samp{P},@samp{E},@samp{Z},@samp{Y}
-suffixes are interpreted as @emph{SI} values, and numbers with @samp{Ki},
-@samp{Mi},@samp{Gi},@samp{Ti},@samp{Pi},@samp{Ei},@samp{Zi},@samp{Yi} suffixes
+with single-letter suffixes like @samp{K}
+suffixes are interpreted as @emph{SI} values, and numbers with
+two-letter suffixes like @samp{Ki}
 are interpreted as @emph{IEC} values.
 
 @end table
diff --git a/src/dd.c b/src/dd.c
index 190a721b3..6d448a57b 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -573,7 +573,7 @@ Copy a file, converting and formatting according to the operands.\n\
 \n\
 N and BYTES may be followed by the following multiplicative suffixes:\n\
 c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024, xM=M,\n\
-GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.\n\
+GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y, R, Q.\n\
 Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\
 If N ends in 'B', it counts bytes not blocks.\n\
 \n\
diff --git a/src/du.c b/src/du.c
index 71d6edd3d..8bffc1aa7 100644
--- a/src/du.c
+++ b/src/du.c
@@ -840,7 +840,7 @@ main (int argc, char **argv)
         case 't':
           {
             enum strtol_error e;
-            e = xstrtoimax (optarg, NULL, 0, &opt_threshold, "kKmMGTPEZY0");
+            e = xstrtoimax (optarg, NULL, 0, &opt_threshold, "kKmMGTPEZYRQ0");
             if (e != LONGINT_OK)
               xstrtol_fatal (e, oi, c, long_options, optarg);
             if (opt_threshold == 0 && *optarg == '-')
diff --git a/src/head.c b/src/head.c
index 63674d769..c9d3b0d05 100644
--- a/src/head.c
+++ b/src/head.c
@@ -140,7 +140,7 @@ With more than one FILE, precede each with a header giving the file name.\n\
 \n\
 NUM may have a multiplier suffix:\n\
 b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,\n\
-GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.\n\
+GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y, R, Q.\n\
 Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\
 "), stdout);
       emit_ancillary_info (PROGRAM_NAME);
@@ -910,7 +910,7 @@ head_file (char const *filename, uintmax_t n_units, bool count_lines,
 static uintmax_t
 string_to_integer (bool count_lines, char const *n_string)
 {
-  return xdectoumax (n_string, 0, UINTMAX_MAX, "bkKmMGTPEZY0",
+  return xdectoumax (n_string, 0, UINTMAX_MAX, "bkKmMGTPEZYRQ0",
                      count_lines ? _("invalid number of lines")
                                  : _("invalid number of bytes"), 0);
 }
diff --git a/src/numfmt.c b/src/numfmt.c
index e917b0dc6..2067ca164 100644
--- a/src/numfmt.c
+++ b/src/numfmt.c
@@ -161,10 +161,10 @@ enum { DELIMITER_DEFAULT = CHAR_MAX + 1 };
 enum { MAX_UNSCALED_DIGITS = LDBL_DIG };
 
 /* Maximum number of digits we can work with.
-   This is equivalent to 999Y.
+   This is equivalent to 999Q.
    NOTE: 'long double' can handle more than that, but there's
-         no official suffix assigned beyond Yotta (1000^8).  */
-enum { MAX_ACCEPTABLE_DIGITS = 27 };
+         no official suffix assigned beyond Quetta (1000^10).  */
+enum { MAX_ACCEPTABLE_DIGITS = 33 };
 
 static enum scale_type scale_from = scale_none;
 static enum scale_type scale_to = scale_none;
@@ -229,11 +229,13 @@ default_scale_base (enum scale_type scale)
     }
 }
 
-static inline int
+static char const zero_and_valid_suffixes[] = "0KMGTPEZYRQ";
+static char const *valid_suffixes = 1 + zero_and_valid_suffixes;
+
+static inline bool
 valid_suffix (const char suf)
 {
-  static char const *valid_suffixes = "KMGTPEZY";
-  return (strchr (valid_suffixes, suf) != NULL);
+  return strchr (valid_suffixes, suf) != NULL;
 }
 
 static inline int
@@ -265,6 +267,12 @@ suffix_power (const char suf)
     case 'Y':                  /* yotta or 2**80.  */
       return 8;
 
+    case 'R':                  /* ronna or 2**90.  */
+      return 9;
+
+    case 'Q':                  /* quetta or 2**100.  */
+      return 10;
+
     default:                   /* should never happen. assert?  */
       return 0;
     }
@@ -302,6 +310,12 @@ suffix_power_char (unsigned int power)
     case 8:
       return "Y";
 
+    case 9:
+      return "R";
+
+    case 10:
+      return "Q";
+
     default:
       return "(error)";
     }
@@ -460,7 +474,7 @@ enum simple_strtod_error
    Returns:
       SSE_OK - valid number.
       SSE_OK_PRECISION_LOSS - if more than 18 digits were used.
-      SSE_OVERFLOW          - if more than 27 digits (999Y) were used.
+      SSE_OVERFLOW          - if more than 33 digits (999Q) were used.
       SSE_INVALID_NUMBER    - if no digits were found.  */
 static enum simple_strtod_error
 simple_strtod_int (char const *input_str,
@@ -525,7 +539,7 @@ simple_strtod_int (char const *input_str,
    Returns:
       SSE_OK - valid number.
       SSE_OK_PRECISION_LOSS - if more than 18 digits were used.
-      SSE_OVERFLOW          - if more than 27 digits (999Y) were used.
+      SSE_OVERFLOW          - if more than 33 digits (999Q) were used.
       SSE_INVALID_NUMBER    - if no digits were found.  */
 static enum simple_strtod_error
 simple_strtod_float (char const *input_str,
@@ -598,7 +612,7 @@ simple_strtod_float (char const *input_str,
    Returns:
       SSE_OK - valid number.
       SSE_OK_PRECISION_LOSS - if more than LDBL_DIG digits were used.
-      SSE_OVERFLOW          - if more than 27 digits (999Y) were used.
+      SSE_OVERFLOW          - if more than 33 digits (999Q) were used.
       SSE_INVALID_NUMBER    - if no digits were found.
       SSE_VALID_BUT_FORBIDDEN_SUFFIX
       SSE_INVALID_SUFFIX
@@ -830,7 +844,7 @@ unit_to_umax (char const *n_string)
   size_t n_len = strlen (n_string);
   char *end = NULL;
   uintmax_t n;
-  char const *suffixes = "KMGTPEZY";
+  char const *suffixes = valid_suffixes;
 
   /* Adjust suffixes so K=1000, Ki=1024, KiB=invalid.  */
   if (n_len && ! c_isdigit (n_string[n_len - 1]))
@@ -845,7 +859,7 @@ unit_to_umax (char const *n_string)
         {
           *++end = 'B';
           *++end = '\0';
-          suffixes = "KMGTPEZY0";
+          suffixes = zero_and_valid_suffixes;
         }
 
       c_string = t_string;
@@ -1224,7 +1238,7 @@ prepare_padded_number (const long double val, size_t precision)
     {
       if (inval_style != inval_ignore)
         error (conv_exit_code, 0, _("value too large to be printed: '%Lg'"
-                                    " (cannot handle values > 999Y)"), val);
+                                    " (cannot handle values > 999Q)"), val);
       return 0;
     }
 
diff --git a/src/od.c b/src/od.c
index e3f21b3cb..9e9142760 100644
--- a/src/od.c
+++ b/src/od.c
@@ -419,7 +419,7 @@ BYTES is hex with 0x or 0X prefix, and may have a multiplier suffix:\n\
   K    1024\n\
   MB   1000*1000\n\
   M    1024*1024\n\
-and so on for G, T, P, E, Z, Y.\n\
+and so on for G, T, P, E, Z, Y, R, Q.\n\
 Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\
 "), stdout);
       emit_ancillary_info (PROGRAM_NAME);
diff --git a/src/shred.c b/src/shred.c
index b4f885cd4..3ec8fe1f0 100644
--- a/src/shred.c
+++ b/src/shred.c
@@ -1216,7 +1216,7 @@ main (int argc, char **argv)
           break;
 
         case 's':
-          flags.size = xnumtoumax (optarg, 0, 0, OFF_T_MAX, "cbBkKMGTPEZY0",
+          flags.size = xnumtoumax (optarg, 0, 0, OFF_T_MAX, "cbBkKMGTPEZYRQ0",
                                    _("invalid file size"), 0);
           break;
 
diff --git a/src/sort.c b/src/sort.c
index a99f01072..5f159374b 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -519,7 +519,7 @@ the entire line as the key.  Use --debug to diagnose incorrect key usage.\n\
 SIZE may be followed by the following multiplicative suffixes:\n\
 "), stdout);
       fputs (_("\
-% 1% of memory, b 1, K 1024 (default), and so on for M, G, T, P, E, Z, Y.\n\
+% 1% of memory, b 1, K 1024 (default), and so on for M, G, T, P, E, Z, Y, R, Q.\n\
 \n\
 *** WARNING ***\n\
 The locale specified by the environment affects sort order.\n\
@@ -1877,28 +1877,30 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file)
 static char const unit_order[UCHAR_LIM] =
   {
 #if ! ('K' == 75 && 'M' == 77 && 'G' == 71 && 'T' == 84 && 'P' == 80 \
-     && 'E' == 69 && 'Z' == 90 && 'Y' == 89 && 'k' == 107)
+       && 'E' == 69 && 'Z' == 90 && 'Y' == 89 && 'R' == 82 && 'Q' == 81 \
+       && 'k' == 107)
     /* This initializer syntax works on all C99 hosts.  For now, use
        it only on non-ASCII hosts, to ease the pain of porting to
        pre-C99 ASCII hosts.  */
     ['K']=1, ['M']=2, ['G']=3, ['T']=4, ['P']=5, ['E']=6, ['Z']=7, ['Y']=8,
+    ['R']=9, ['Q']=10,
     ['k']=1,
 #else
     /* Generate the following table with this command:
-       perl -e 'my %a=(k=>1, K=>1, M=>2, G=>3, T=>4, P=>5, E=>6, Z=>7, Y=>8);
+       perl -e 'my %a=(k=>1,K=>1,M=>2,G=>3,T=>4,P=>5,E=>6,Z=>7,Y=>8,R=>9,Q=>10);
        foreach my $i (0..255) {my $c=chr($i); $a{$c} ||= 0;print "$a{$c}, "}'\
        |fmt  */
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3,
-    0, 0, 0, 1, 0, 2, 0, 0, 5, 0, 0, 0, 4, 0, 0, 0, 0, 8, 7, 0, 0, 0, 0, 0,
+    0, 0, 0, 1, 0, 2, 0, 0, 5, 10, 9, 0, 4, 0, 0, 0, 0, 8, 7, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 #endif
   };
 
@@ -1972,7 +1974,7 @@ find_unit_order (char const *number)
 }
 
 /* Compare numbers A and B ending in units with SI or IEC prefixes
-       <none/unknown> < K/k < M < G < T < P < E < Z < Y  */
+       <none/unknown> < K/k < M < G < T < P < E < Z < Y < R < Q */
 
 ATTRIBUTE_PURE
 static int
diff --git a/src/stdbuf.c b/src/stdbuf.c
index f3eb9b016..2d7aec28c 100644
--- a/src/stdbuf.c
+++ b/src/stdbuf.c
@@ -110,7 +110,7 @@ If MODE is '0' the corresponding stream will be unbuffered.\n\
 "), stdout);
       fputs (_("\n\
 Otherwise MODE is a number which may be followed by one of the following:\n\
-KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
+KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G,T,P,E,Z,Y,R,Q.\n\
 Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\
 In this case the corresponding stream will be fully buffered with the buffer\n\
 size set to MODE bytes.\n\
diff --git a/src/system.h b/src/system.h
index 52ed378d1..2aa5d6978 100644
--- a/src/system.h
+++ b/src/system.h
@@ -593,7 +593,7 @@ emit_size_note (void)
 {
   fputs (_("\n\
 The SIZE argument is an integer and optional unit (example: 10K is 10*1024).\n\
-Units are K,M,G,T,P,E,Z,Y (powers of 1024) or KB,MB,... (powers of 1000).\n\
+Units are K,M,G,T,P,E,Z,Y,R,Q (powers of 1024) or KB,MB,... (powers of 1000).\n\
 Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\
 "), stdout);
 }
diff --git a/src/tail.c b/src/tail.c
index 35bc55130..2244509dd 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -319,7 +319,7 @@ With more than one FILE, precede each with a header giving the file name.\n\
 \n\
 NUM may have a multiplier suffix:\n\
 b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,\n\
-GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.\n\
+GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y, R, Q.\n\
 Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\
 \n\
 "), stdout);
@@ -2194,7 +2194,7 @@ parse_options (int argc, char **argv,
           else if (*optarg == '-')
             ++optarg;
 
-          *n_units = xdectoumax (optarg, 0, UINTMAX_MAX, "bkKmMGTPEZY0",
+          *n_units = xdectoumax (optarg, 0, UINTMAX_MAX, "bkKmMGTPEZYRQ0",
                                  count_lines
                                  ? _("invalid number of lines")
                                  : _("invalid number of bytes"), 0);
diff --git a/tests/misc/numfmt.pl b/tests/misc/numfmt.pl
index 06df09a44..31a87dc14 100755
--- a/tests/misc/numfmt.pl
+++ b/tests/misc/numfmt.pl
@@ -116,29 +116,29 @@ my @Tests =
 
      # Test Suffix logic
      ['suf-1', '4000',    {OUT=>'4000'}],
-     ['suf-2', '4Q',
-             {ERR => "$prog: invalid suffix in input: '4Q'\n"},
+     ['suf-2', '4J',
+             {ERR => "$prog: invalid suffix in input: '4J'\n"},
              {EXIT => '2'}],
      ['suf-2.1', '4M',
              {ERR => "$prog: rejecting suffix " .
              "in input: '4M' (consider using --from)\n"},
              {EXIT => '2'}],
      ['suf-3', '--from=si 4M',  {OUT=>'4000000'}],
-     ['suf-4', '--from=si 4Q',
-             {ERR => "$prog: invalid suffix in input: '4Q'\n"},
+     ['suf-4', '--from=si 4J',
+             {ERR => "$prog: invalid suffix in input: '4J'\n"},
              {EXIT => '2'}],
-     ['suf-5', '--from=si 4MQ',
-             {ERR => "$prog: invalid suffix in input '4MQ': 'Q'\n"},
+     ['suf-5', '--from=si 4MJ',
+             {ERR => "$prog: invalid suffix in input '4MJ': 'J'\n"},
              {EXIT => '2'}],
 
      ['suf-6', '--from=iec 4M',  {OUT=>'4194304'}],
      ['suf-7', '--from=auto 4M',  {OUT=>'4000000'}],
      ['suf-8', '--from=auto 4Mi',  {OUT=>'4194304'}],
-     ['suf-9', '--from=auto 4MiQ',
-             {ERR => "$prog: invalid suffix in input '4MiQ': 'Q'\n"},
+     ['suf-9', '--from=auto 4MiJ',
+             {ERR => "$prog: invalid suffix in input '4MiJ': 'J'\n"},
              {EXIT => '2'}],
-     ['suf-10', '--from=auto 4QiQ',
-             {ERR => "$prog: invalid suffix in input: '4QiQ'\n"},
+     ['suf-10', '--from=auto 4JiJ',
+             {ERR => "$prog: invalid suffix in input: '4JiJ'\n"},
              {EXIT => '2'}],
 
      # characters after a white space are OK - printed as-is
@@ -443,8 +443,8 @@ my @Tests =
              {EXIT=>2}],
 
      # INVALID_SUFFIX
-     ['strtod-9', '--from=si 12.2Q',
-             {ERR=>"$prog: invalid suffix in input: '12.2Q'\n"},
+     ['strtod-9', '--from=si 12.2J',
+             {ERR=>"$prog: invalid suffix in input: '12.2J'\n"},
              {EXIT=>2}],
 
      # VALID_BUT_FORBIDDEN_SUFFIX
@@ -731,18 +731,18 @@ my @Tests =
      ## Check all errors again, this time with --invalid=fail
      ##  Input will be printed without conversion,
      ##  and exit code will be 2
-     ['ign-err-1', '--invalid=fail 4Q',
-             {ERR => "$prog: invalid suffix in input: '4Q'\n"},
-             {OUT => "4Q\n"},
+     ['ign-err-1', '--invalid=fail 4J',
+             {ERR => "$prog: invalid suffix in input: '4J'\n"},
+             {OUT => "4J\n"},
              {EXIT => 2}],
      ['ign-err-2', '--invalid=fail 4M',
              {ERR => "$prog: rejecting suffix " .
              "in input: '4M' (consider using --from)\n"},
              {OUT => "4M\n"},
              {EXIT => 2}],
-     ['ign-err-3', '--invalid=fail --from=si 4MQ',
-             {ERR => "$prog: invalid suffix in input '4MQ': 'Q'\n"},
-             {OUT => "4MQ\n"},
+     ['ign-err-3', '--invalid=fail --from=si 4MJ',
+             {ERR => "$prog: invalid suffix in input '4MJ': 'J'\n"},
+             {OUT => "4MJ\n"},
              {EXIT => 2}],
      ['ign-err-4', '--invalid=fail --suffix=Foo --to=si   7000FooF',
               {ERR => "$prog: invalid suffix in input: '7000FooF'\n"},
@@ -851,7 +851,7 @@ my @Limit_Tests =

      # Test input:
-     # Up to 27 digits is OK.
+     # Up to 33 digits is OK.
      ['large-3.1', '--to=si                           1', {OUT=>   "1"}],
      ['large-3.2', '--to=si                          10', {OUT=>  "10"}],
      ['large-3.3', '--to=si                         100', {OUT=> "100"}],
@@ -879,11 +879,17 @@ my @Limit_Tests =
      ['large-3.25','--to=si   6543210000000000000000000', {OUT=>"6.6Y"}],
      ['large-3.26','--to=si  76543210000000000000000000', {OUT=> "77Y"}],
      ['large-3.27','--to=si 876543210000000000000000000', {OUT=>"877Y"}],
-
-     # More than 27 digits is not OK
-     ['large-3.28','--to=si 9876543210000000000000000000',
+     ['large-3.28','--to=si      9876543210000000000000000000', {OUT=>"9.9R"}],
+     ['large-3.29','--to=si     19876543210000000000000000000', {OUT=> "20R"}],
+     ['large-3.30','--to=si    219876543210000000000000000000', {OUT=>"220R"}],
+     ['large-3.31','--to=si   3219876543210000000000000000000', {OUT=>"3.3Q"}],
+     ['large-3.32','--to=si  43219876543210000000000000000000', {OUT=> "44Q"}],
+     ['large-3.33','--to=si 543219876543210000000000000000000', {OUT=>"544Q"}],
+
+     # More than 33 digits is not OK
+     ['large-3.34','--to=si 6543219876543210000000000000000000',
              {ERR => "$prog: value too large to be converted: " .
-                     "'9876543210000000000000000000'\n"},
+                     "'6543219876543210000000000000000000'\n"},
              {EXIT => 2}],
 
      # Test Output
@@ -952,15 +958,15 @@ my @Limit_Tests =
              {EXIT => 2}],
      ['large-13.1','--from=si --from-unit=1000000 --to=si 9P', {OUT=>"9.0Z"}],
 
-     # Numbers>999Y are never acceptable, regardless of scaling
-     ['large-14','--from=si --to=si 999Y', {OUT=>"999Y"}],
-     ['large-14.1','--from=si --to=si 1000Y',
-             {ERR => "$prog: value too large to be printed: '1e+27' " .
-                     "(cannot handle values > 999Y)\n"},
+     # Numbers>999Q are never acceptable, regardless of scaling
+     ['large-14','--from=si --to=si 999Q', {OUT=>"999Q"}],
+     ['large-14.1','--from=si --to=si 1000Q',
+             {ERR => "$prog: value too large to be printed: '1e+33' " .
+                     "(cannot handle values > 999Q)\n"},
              {EXIT => 2}],
-     ['large-14.2','--from=si --to=si --from-unit=10000 1Y',
-             {ERR => "$prog: value too large to be printed: '1e+28' " .
-                     "(cannot handle values > 999Y)\n"},
+     ['large-14.2','--from=si --to=si --from-unit=10000 1Q',
+             {ERR => "$prog: value too large to be printed: '1e+34' " .
+                     "(cannot handle values > 999Q)\n"},
              {EXIT => 2}],
 
      # intmax_t overflow when rounding caused this to fail before 8.24
@@ -999,10 +1005,10 @@ my @Limit_Tests =
                      "(consider using --to)\n"},
              {OUT => "10000000000000000000\n"},
              {EXIT=>2}],
-     ['ign-err-11','--invalid=fail --to=si 9876543210000000000000000000',
+     ['ign-err-11','--invalid=fail --to=si 6543219876543210000000000000000000',
              {ERR => "$prog: value too large to be converted: " .
-                     "'9876543210000000000000000000'\n"},
-             {OUT => "9876543210000000000000000000\n"},
+                     "'6543219876543210000000000000000000'\n"},
+             {OUT => "6543219876543210000000000000000000\n"},
              {EXIT => 2}],
   );
 # Restrict these tests to systems with LDBL_DIG == 18
@@ -1067,7 +1073,7 @@ push @Tests, @Locale_Tests if $locale ne 'C';
 
 ## Check all valid/invalid suffixes
 foreach my $suf ( 'A' .. 'Z', 'a' .. 'z' ) {
-  if ( $suf =~ /^[KMGTPEZY]$/ )
+  if ( $suf =~ /^[KMGTPEZYRQ]$/ )
     {
       push @Tests, ["auto-suf-si-$suf","--from=si --to=si 1$suf",
               {OUT=>"1.0$suf"}];
diff --git a/tests/misc/sort.pl b/tests/misc/sort.pl
index 0cd2340a2..46f1d7acd 100755
--- a/tests/misc/sort.pl
+++ b/tests/misc/sort.pl
@@ -56,10 +56,10 @@ my @Tests =
 
 # human readable suffixes
 ["h1", '-h',
- {IN=>"1Y\n1Z\n1E\n1P\n1T\n1G\n1M\n1K\n02\n1\nY\n-1k\n-1M\n-1G\n-1T\n"
-      . "-1P\n-1E\n-1Z\n-1Y\n"},
- {OUT=>"-1Y\n-1Z\n-1E\n-1P\n-1T\n-1G\n-1M\n-1k\nY\n1\n02\n1K\n1M\n1G\n1T\n"
-      . "1P\n1E\n1Z\n1Y\n"}],
+ {IN=>"1Q\n1R\n1Y\n1Z\n1E\n1P\n1T\n1G\n1M\n1K\n02\n1\nY\n-1k\n-1M\n-1G\n-1T\n"
+      . "-1P\n-1E\n-1Z\n-1Y\n-1Q\n-1R\n"},
+ {OUT=>"-1Q\n-1R\n-1Y\n-1Z\n-1E\n-1P\n-1T\n-1G\n-1M\n-1k\n"
+      . "Y\n1\n02\n1K\n1M\n1G\n1T\n1P\n1E\n1Z\n1Y\n1R\n1Q\n"}],
 ["h2", '-h', {IN=>"1M\n-2G\n-3K"}, {OUT=>"-2G\n-3K\n1M\n"}],
 # check that it works with powers of 1024
 ["h3", '-k 2,2h -k 1,1', {IN=>"a 1G\nb 1023M\n"}, {OUT=>"b 1023M\na 1G\n"}],
-- 
2.37.2