diff -r 07a3ceab58d6 scripts/strings/dec2base.m
--- a/scripts/strings/dec2base.m	Thu Oct 27 10:38:47 2022 -0400
+++ b/scripts/strings/dec2base.m	Thu Oct 27 15:23:45 2022 -0400
@@ -26,8 +26,9 @@
 ## -*- texinfo -*-
 ## @deftypefn  {} {@var{str} =} dec2base (@var{d}, @var{base})
 ## @deftypefnx {} {@var{str} =} dec2base (@var{d}, @var{base}, @var{len})
+## @deftypefnx {} {@var{str} =} dec2base (@var{d}, @var{base}, @var{len}, @var{decimals})
 ## Return a string of symbols in base @var{base} corresponding to the
-## non-negative integer @var{d}.
+## value @var{d}.
 ##
 ## @example
 ## @group
@@ -53,10 +54,15 @@
 ##
 ## The optional third argument, @var{len}, specifies the minimum number of
 ## digits in the result.
+##
+## The optional fourth argument, @var{decimals}, specifies the number of
+## digits in the result after the radix point (the "decimal point" for bases
+## other than 10).
+##
 ## @seealso{base2dec, dec2bin, dec2hex}
 ## @end deftypefn
 
-function str = dec2base (d, base, len)
+function str = dec2base (d, base, len, decimals = 0)
 
   if (nargin < 2)
     print_usage ();
@@ -72,10 +78,18 @@ function str = dec2base (d, base, len)
   ## Treat logical as numeric for compatibility with ML
   if (islogical (d))
     d = double (d);
-  elseif (! isnumeric (d) || iscomplex (d) || any (d < 0 | d != fix (d)))
-    error ("dec2base: input must be real non-negative integers");
+  elseif (! isnumeric (d) || iscomplex (d))
+    error ("dec2base: input must be real numbers");
   endif
 
+  ## Note which elements are negative for processing later
+  neg = (d < 0);
+  d(neg) = -d(neg);
+
+  ## Pull out the fractional part for processing later
+  fracpart = d - floor (d);
+  d = floor (d);
+
   symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
   if (ischar (base))
     symbols = base(:).';  # force a row vector
@@ -94,7 +108,7 @@ function str = dec2base (d, base, len)
   ## by 1 digit
   max_len = round (log (max (max (d), 1)) / log (base)) + 1;
 
-  if (nargin == 3)
+  if (nargin >= 3)
     if (! (isscalar (len) && isreal (len) && len >= 0 && len == fix (len)))
       error ("dec2base: LEN must be a non-negative integer");
     endif
@@ -102,21 +116,77 @@ function str = dec2base (d, base, len)
   endif
 
   ## determine digits for each number
-  digits = zeros (length (d), max_len);
+  digits = zeros (numel (d), max_len);
   for k = max_len:-1:1
     digits(:,k) = mod (d, base);
     d = round ((d - digits(:,k)) / base);
   endfor
 
-  ## convert digits to symbols
+  ## Compute any fractional part and append
+  if (nargin == 4 && decimals > 0)
+    digits2 = zeros (numel (d), decimals);
+    for k = 1:decimals
+      fracpart *= base;
+      digits2(:,k) = floor (fracpart);
+      fracpart -= floor (fracpart);
+    endfor
+  else
+    digits2 = zeros (rows (digits), 0);
+  endif
+
+  ## Handle negative inputs now
+  for k = find (neg)(:)'
+    digits (k, :) = (base-1) - digits (k, :);
+    if (! isempty (digits2))
+      digits2 (k, :) = (base-1) - digits2 (k, :);
+    endif
+
+    if (! isempty (digits2))
+      j = columns (digits2);
+      digits2 (k, j) += 1;  # this is a generalization of two's complement
+      while (digits2(j) >= base && j > 1)
+        digits2(k, j) -= base;
+        digits2(k, j-1) += 1;
+        j -= 1;
+      endwhile
+      if (digits2(k, 1) >= base)  # carry over to integer part
+        digits2(k, 1) -= base;
+        digits(k, end) += 1;
+      endif
+    else  # no fractional part ==> increment integer part
+      digits(k, end) += 1;
+    end
+
+    j = columns (digits);
+    while (digits(j) >= base && j > 1)
+      digits(k, j) -= base;
+      digits(k, j-1) += 1;
+      j -= 1;
+    endwhile
+
+    if (digits(k, 1) >= base)  # augment by one place if really needed
+      digits(k, 1) -= base;
+      digits = [zeros(rows(digits), 1), digits];
+      digits(k, 1) += 1;
+    endif
+  endfor
+
+  ## Convert digits to symbols: integer part
   str = reshape (symbols(digits+1), size (digits));
 
+  ## Convert digits to symbols: fractional part
+  ## Append fractional part to str if needed.
+  if (! isempty (digits2))
+    str2 = reshape (symbols(digits2+1), size (digits2));
+    str = [str, repmat('.', rows(str), 1), str2];
+  endif
+
   ## Check if the first element is the zero symbol.  It seems possible
   ## that LEN is provided, and is less than the computed MAX_LEN and
   ## MAX_LEN is computed to be one larger than necessary, so we would
   ## have a leading zero to remove.  But if LEN >= MAX_LEN, we should
   ## not remove any leading zeros.
-  if ((nargin == 2 || (nargin == 3 && max_len > len))
+  if ((nargin == 2 || (nargin >= 3 && max_len > len))
       && columns (str) != 1 && ! any (str(:,1) != symbols(1)))
     str = str(:,2:end);
   endif
@@ -135,12 +205,30 @@ endfunction
 %!   s0 = [s0,'0'];
 %! endfor
 
+## Test positive fractional inputs
+%!assert (dec2base (pi,  2, 0, 16), "11.0010010000111111")
+%!assert (dec2base ( e,  2, 2, 16), "10.1011011111100001")
+%!assert (dec2base (pi,  3, 0, 16), "10.0102110122220102")
+%!assert (dec2base ( e,  3, 0, 16), "2.2011011212211020")
+%!assert (dec2base (pi, 16, 0, 10), "3.243F6A8885")
+%!assert (dec2base ( e, 16, 0, 10), "2.B7E151628A")
+
+## Test negative inputs: all correct in complement notation
+%!assert (dec2base (-1, 10), "9")
+%!assert (dec2base (-1, 10, 3), "999")
+%!assert (dec2base (-1, 10, 3, 2), "999.00")
+%!assert (dec2base (-1.1, 10, 3, 2), "998.90")
+%!assert (dec2base (-pi, 2, 8, 16), "11111100.1101101111000001")
+%!assert (dec2base (-e, 2, 8, 16), "11111101.0100100000011111")
+%!assert (dec2base (-pi, 3, 8, 16), "22222212.2120112100002121")
+%!assert (dec2base (-pi, 16, 8, 10), "FFFFFFFC.DBC095777B")
+
 %!test
 %! digits = "0123456789ABCDEF";
 %! for n = 1:13
 %!   for b = 2:16
 %!     pm = dec2base (b^n-1, b);
-%!     assert (length (pm), n);
+%!     assert (numel (pm), n);
 %!     assert (all (pm == digits(b)));
 %!   endfor
 %! endfor
@@ -166,10 +254,8 @@ endfunction
 ## Test input validation
 %!error <Invalid call> dec2base ()
 %!error <Invalid call> dec2base (1)
-%!error <input must be real non-negative integers> dec2base ("A", 10)
-%!error <input must be real non-negative integers> dec2base (2i, 10)
-%!error <input must be real non-negative integers> dec2base (-1, 10)
-%!error <input must be real non-negative integers> dec2base (1.1, 10)
+%!error <dec2base: input must be real numbers> dec2base ("A", 10)
+%!error <dec2base: input must be real numbers> dec2base (2i, 10)
 %!error <symbols representing digits must be unique> dec2base (1, "ABA")
 %!error <whitespace characters are not valid symbols> dec2base (1, "A B")
 %!error <BASE must be an integer> dec2base (1, ones (2))
