Commatizing numbers

From Rosetta Code
Task
Commatizing numbers
You are encouraged to solve this task according to the task description, using any language you may know.

Commatizing   numbers (as used here, is a handy expedient made-up word) is the act of adding commas to a number (or string), or to the numeric part of a larger string.


Task

Write a function that takes a string as an argument with optional arguments or parameters (the format of parameters/options is left to the programmer) that in general, adds commas (or some other characters, including blanks or tabs) to the first numeric part of a string (if it's suitable for commatizing as per the rules below), and returns that newly commatized string.

Some of the commatizing rules (specified below) are arbitrary, but they'll be a part of this task requirements, if only to make the results consistent amongst national preferences and other disciplines.

The number may be part of a larger (non-numeric) string such as:

  •   «US$1744 millions»       ──or──
  •   ±25000 motes.


The string may possibly not have a number suitable for commatizing, so it should be untouched and no error generated.

If any argument (option) is invalid, nothing is changed and no error need be generated (quiet execution, no fail execution).   Error message generation is optional.

The exponent part of a number is never commatized.   The following string isn't suitable for commatizing:   9.7e+12000

Leading zeroes are never commatized.   The string   0000000005714.882   after commatization is:   0000000005,714.882

Any   period   (.)   in a number is assumed to be a   decimal point.

The original string is never changed   except   by the addition of commas   [or whatever character(s) is/are used for insertion], if at all.

To wit, the following should be preserved:

  •   leading signs (+, -)       ── even superfluous signs
  •   leading/trailing/embedded blanks, tabs, and other whitespace
  •   the case (upper/lower) of the exponent indicator, e.g.:   4.8903d-002


Any exponent character(s) should be supported:

  •   1247e12
  •   57256.1D-4
  •   4444^60
  •   750010**35
  •   8500x10**35
  •   9500↑35
  •   +55000↑3
  •   1000**100
  •   2048²
  •   409632
  •   10000pow(pi)


Numbers may be terminated with any non-digit character, including subscripts and/or superscript:   41421356243   or   7320509076(base 24).

The character(s) to be used for the comma can be specified, and may contain blanks, tabs, and other whitespace characters, as well as multiple characters.   The default is the comma (,) character.

The   period length   can be specified   (sometimes referred to as "thousands" or "thousands separators").   The   period length   can be defined as the length (or number) of the decimal digits between commas.   The default period length is   3.

E.G.:   in this example, the   period length   is five:   56789,12340,14148

The location of where to start the scanning for the target field (the numeric part) should be able to be specified.   The default is   1.

The character strings below may be placed in a file (and read) or stored as simple strings within the program.


Strings to be used as a minimum

The value of   pi   (expressed in base 10)   should be separated with blanks every   5   places past the decimal point,
the Zimbabwe dollar amount should use a decimal point for the "comma" separator:

  •   pi=3.14159265358979323846264338327950288419716939937510582097494459231
  •   The author has two Z$100000000000000 Zimbabwe notes (100 trillion).
  •   "-in Aus$+1411.8millions"
  •   ===US$0017440 millions=== (in 2000 dollars)
  •   123.e8000 is pretty big.
  •   The land area of the earth is 57268900(29% of the surface) square miles.
  •   Ain't no numbers in this here words, nohow, no way, Jose.
  •   James was never known as 0000000007
  •   Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.
  •   ␢␢␢$-140000±100 millions.
  •   6/9/1946 was a good year for some.


where the penultimate string has three leading blanks   (real blanks are to be used).


Also see

11l

Translation of: Nim
F commatize(s, period = 3, sep = ‘,’)
   V m = re:‘(\.[0-9]+|[1-9]([0-9]+)?(\.[0-9]+)?)’.search(s)
   I !m
      R s

   V match = m.group()
   V splits = match.split(‘.’)

   V ip = splits[0]
   I ip.len > period
      V inserted = 0
      L(i) ((ip.len - 1) % period + 1 .< ip.len).step(period)
         ip = ip[0 .< i + inserted]‘’sep‘’ip[i + inserted ..]
         inserted += sep.len

   I splits.len > 1
      V dp = splits[1]
      I dp.len > period
         L(i) ((dp.len - 1) I/ period * period .< period - 1).step(-period)
            dp = dp[0 .< i]‘’sep‘’dp[i..]
      ip ‘’= ‘.’dp

   R s[0 .< m.start()]‘’ip‘’s[m.end()..]

V tests = [‘123456789.123456789’,
           ‘.123456789’,
           ‘57256.1D-4’,
           ‘pi=3.14159265358979323846264338327950288419716939937510582097494459231’,
           ‘The author has two Z$100000000000000 Zimbabwe notes (100 trillion).’,
           ‘-in Aus$+1411.8millions’,
           ‘===US$0017440 millions=== (in 2000 dollars)’,
           ‘123.e8000 is pretty big.’,
           ‘The land area of the earth is 57268900(29% of the surface) square miles.’,
           ‘Ain't no numbers in this here words, nohow, no way, Jose.’,
           ‘James was never known as 0000000007’,
           ‘Arthur Eddington wrote: I believe there are ’""
             ‘15747724136275002577605653961181555468044717914527116709366231425076185631031296’""
             ‘ protons in the universe.’,
           ‘   $-140000±100 millions.’,
           ‘6/9/1946 was a good year for some.’]

print(commatize(tests[0], period' 2, sep' ‘*’))
print(commatize(tests[1], period' 3, sep' ‘-’))
print(commatize(tests[2], period' 4, sep' ‘__’))
print(commatize(tests[3], period' 5, sep' ‘ ’))
print(commatize(tests[4], sep' ‘.’))

L(test) tests[5..]
   print(commatize(test))
Output:
1*23*45*67*89.12*34*56*78*9
.123-456-789
5__7256.1D-4
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
-in Aus$+1,411.8millions
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

ALGOL 68

# returns text commatized according to the rules of the task and the      #
#         period, location and separator paramters                        #
PROC commatize = ( STRING text, INT location, INT period, STRING separator )STRING:
     IF  STRING str := text[ AT 1 ];
         # handle the options                                             #
         INT    start position   := IF location  = 0  THEN  1  ELSE location  FI;
         INT    period length    := IF period    = 0  THEN  3  ELSE period    FI;
         STRING separator string := IF separator = "" THEN "," ELSE separator FI;
         period length < 1 OR start position < 1 OR start position > UPB str
     THEN
         # invalid parameters - return the text unchanged                 #
         text
     ELIF # attempt to find a non-zero digit                              #
          INT number pos := start position;
          WHILE IF number pos > UPB str
                THEN FALSE
                ELSE str[ number pos ] < "1" OR str[ number pos ] > "9"
                FI
          DO
              number pos +:= 1
          OD;
          number pos > UPB str
     THEN # no digits in the string - return the text unchanged           #
          text
     ELSE # have at least one digit                                       #
          STRING result := str[ 1 : number pos - 1 ];
          # find the final digit                                          #
          INT number end := number pos;
          WHILE IF number end >= UPB str
                THEN FALSE
                ELSE str[ number end + 1 ] >= "0" AND str[ number end + 1 ] <= "9"
                FI
          DO
              number end +:= 1
          OD;
          # copy the digits commatizing as required                       #
          INT   digit count := ( number end - number pos ) + 1;
          WHILE digit count > 1 DO
              result      +:= str[ number pos ];
              number pos  +:= 1;
              digit count -:= 1;
              IF digit count MOD period length = 0 THEN
                  # need a comma after this digit                         #
                  result +:= separator string
              FI
          OD;
          # final digit and the rest of the string                        #
          result +:= str[ number pos : ];
          result
     FI # commatize # ;

# modes and operators to allow us to specify optional parameters to the   #
# commatizing procedure                                                   #
MODE COMMATIZINGOPTIONS = STRUCT( STRING text, INT location, INT period, STRING separator );
PRIO LOCATION  = 9;
OP   LOCATION  = ( STRING text, INT location )COMMATIZINGOPTIONS:     COMMATIZINGOPTIONS( text, location, 0, "" );
PRIO PERIOD    = 9;
OP   PERIOD    = ( STRING text, INT period   )COMMATIZINGOPTIONS:     COMMATIZINGOPTIONS( text, 0, period, "" );
PRIO SEPARATOR = 9;
OP   SEPARATOR = ( STRING text, CHAR   separator )COMMATIZINGOPTIONS: COMMATIZINGOPTIONS( text, 0, 0, separator );
OP   SEPARATOR = ( STRING text, STRING separator )COMMATIZINGOPTIONS: COMMATIZINGOPTIONS( text, 0, 0, separator );
OP   LOCATION  = ( COMMATIZINGOPTIONS opts, INT location     )COMMATIZINGOPTIONS:
                 COMMATIZINGOPTIONS( text OF opts, location, period OF opts, separator OF opts );
OP   PERIOD    = ( COMMATIZINGOPTIONS opts, INT period       )COMMATIZINGOPTIONS:
                 COMMATIZINGOPTIONS( text OF opts, location OF opts, period, separator OF opts );
OP   SEPARATOR = ( COMMATIZINGOPTIONS opts, CHAR   separator )COMMATIZINGOPTIONS:
                 COMMATIZINGOPTIONS( text OF opts, location OF opts, period OF opts, separator );
OP   SEPARATOR = ( COMMATIZINGOPTIONS opts, STRING separator )COMMATIZINGOPTIONS:
                 COMMATIZINGOPTIONS( text OF opts, location OF opts, period OF opts, separator );
OP   COMMATIZE = ( STRING text             )STRING: commatize( text, 0, 0, "" );
OP   COMMATIZE = ( COMMATIZINGOPTIONS opts )STRING:
                 commatize( text OF opts, location OF opts, period OF opts, separator OF opts );

# test the commatization procedure and operators                          #
print( ( COMMATIZE( "pi=3.14159265358979323846264338327950288419716939937510582097494459231" PERIOD 5 SEPARATOR " " LOCATION 6 ),
                                                                                                                       newline ) );
print( ( COMMATIZE( "The author has two Z$100000000000000 Zimbabwe notes (100 trillion)."             SEPARATOR "." ), newline ) );
print( ( COMMATIZE  """-in Aus$+1411.8millions""",                                                                     newline ) );
print( ( COMMATIZE  "===US$0017440 millions=== (in 2000 dollars)",                                                     newline ) );
print( ( COMMATIZE  "123.e8000 is pretty big.",                                                                        newline ) );
print( ( COMMATIZE  "The land area of the earth is 57268900(29% of the surface) square miles.",                        newline ) );
print( ( COMMATIZE  "Ain't no numbers in this here words, nohow, no way, Jose.",                                       newline ) );
print( ( COMMATIZE  "James was never known as 0000000007",                                                             newline ) );
print( ( COMMATIZE  "Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.",
                                                                                                                       newline ) );
print( ( COMMATIZE  "   $-140000±100 millions.",                                                                       newline ) );
print( ( COMMATIZE  "6/9/1946 was a good year for some.",                                                              newline ) )
Output:
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
"-in Aus$+1,411.8millions"
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

C#

static string[] inputs = {
	"pi=3.14159265358979323846264338327950288419716939937510582097494459231",
	"The author has two Z$100000000000000 Zimbabwe notes (100 trillion).",
	"\"-in Aus$+1411.8millions\"",
	"===US$0017440 millions=== (in 2000 dollars)"
};

void Main()
{
	inputs.Select(s => Commatize(s, 0, 3, ","))
              .ToList()
              .ForEach(Console.WriteLine);
}

string Commatize(string text, int startPosition, int interval, string separator)
{
	var matches = Regex.Matches(text.Substring(startPosition), "[0-9]*");
	var x = matches.Cast<Match>().Select(match => Commatize(match, interval, separator, text)).ToList();
	return string.Join("", x);
}


string Commatize(Match match, int interval, string separator, string original)
{
	if (match.Length <= interval)
		return original.Substring(match.Index, 
                match.Index == original.Length ? 0 : Math.Max(match.Length, 1));
	
	return string.Join(separator, match.Value.Split(interval));
}

public static class Extension
{
	public static string[] Split(this string source, int interval)
	{
		return SplitImpl(source, interval).ToArray();
	}
	
	static IEnumerable<string>SplitImpl(string source, int interval)
	{
		for	(int i = 1; i < source.Length; i++)
		{
			if (i % interval != 0) continue;
			
			yield return source.Substring(i - interval, interval);
		}
	}
}

D

Better to have more tests than more features.

import std.stdio, std.regex, std.range;

auto commatize(in char[] txt, in uint start=0, in uint step=3,
        in string ins=",") @safe
in {
    assert(step > 0);
} body {
    if (start > txt.length || step > txt.length)
        return txt;

    // First number may begin with digit or decimal point. Exponents ignored.
    enum decFloField = ctRegex!("[0-9]*\\.[0-9]+|[0-9]+");

    auto matchDec = matchFirst(txt[start .. $], decFloField);
    if (!matchDec)
        return txt;

    // Within a decimal float field:
    // A decimal integer field to commatize is positive and not after a point.
    enum decIntField = ctRegex!("(?<=\\.)|[1-9][0-9]*");
    // A decimal fractional field is preceded by a point, and is only digits.
    enum decFracField = ctRegex!("(?<=\\.)[0-9]+");

    return txt[0 .. start] ~ matchDec.pre ~ matchDec.hit
        .replace!(m => m.hit.retro.chunks(step).join(ins).retro)(decIntField)
        .replace!(m => m.hit.chunks(step).join(ins))(decFracField)
        ~ matchDec.post;
}

unittest {
    // An attempted solution may have one or more of the following errors:
    //    ignoring a number that has only zero before its decimal point
    assert("0.0123456".commatize == "0.012,345,6");
    //    commatizing numbers other than the first 
    assert("1000 2.3000".commatize == "1,000 2.3000");
    //    only commatizing in one direction from the decimal point
    assert("0001123.456789".commatize == "0001,123.456,789");
    //    detecting prefixes such as "Z$" requires detecting other prefixes
    assert(" NZ$300000".commatize == " NZ$300,000");
    //    detecting a decimal field that isn't attached to the first number
    assert(" 2600 and .0125".commatize == " 2,600 and .0125");
    //    ignoring the start value, or confusing base 0 (used here) with base 1
    assert("1 77000".commatize(1) == "1 77,000");
    //    ignoring a number that begins with a point, or treating it as integer
    assert(" .0104004".commatize == " .010,400,4");   
}

void main() {
    "pi=3.14159265358979323846264338327950288419716939937510582097494459231"
        .commatize(0, 5, " ").writeln;
    "The author has two Z$100000000000000 Zimbabwe notes (100 trillion)."
        .commatize(0, 3, ".").writeln;
    foreach (const line; "commatizing_numbers_using_defaults.txt".File.byLine)
        line.commatize.writeln;
}
Output:
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
"-in Aus$+1,411.8millions"
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

Delphi

Translation of: Go
program Commatizing_numbers;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.RegularExpressions,
  system.StrUtils;

const
  PATTERN = '(\.[0-9]+|[1-9]([0-9]+)?(\.[0-9]+)?)';
  TESTS: array[0..13] of string = ('123456789.123456789', '.123456789',
    '57256.1D-4', 'pi=3.14159265358979323846264338327950288419716939937510582097494459231',
    'The author has two Z$100000000000000 Zimbabwe notes (100 trillion).',
    '-in Aus$+1411.8millions', '===US$0017440 millions=== (in 2000 dollars)',
    '123.e8000 is pretty big.',
    'The land area of the earth is 57268900(29% of the surface) square miles.',
    'Ain''t no numbers in this here words, nohow, no way, Jose.',
    'James was never known as 0000000007',
    'Arthur Eddington wrote: I believe there are ' +
    '15747724136275002577605653961181555468044717914527116709366231425076185631031296' +
    ' protons in the universe.', '   $-140000±100 millions.',
    '6/9/1946 was a good year for some.');

var
  regex: TRegEx;

function Commatize(s: string; startIndex, period: integer; sep: string): string;
var
  m: TMatch;
  s1, ip, pi, dp: string;
  splits: TArray<string>;
  i: integer;
begin
  regex := TRegEx.Create(PATTERN);

  if (startIndex < 0) or (startIndex >= s.Length) or (period < 1) or (sep.IsEmpty) then
    exit(s);
  m := regex.Match(s.Substring(startIndex, s.Length));
  if not m.Success then
    exit(s);

  s1 := m.Groups[0].Value;
  splits := s1.Split(['.']);

  ip := splits[0];

  if ip.Length > period then
  begin
    pi := ReverseString(ip);
    i := ((ip.Length - 1) div period) * period;

    while i >= period do
    begin
      pi := pi.Substring(0, i) + sep + pi.Substring(i);
      i := i - period;
    end;
    ip := ReverseString(pi);
  end;

  if s1.Contains('.') then
  begin
    dp := splits[1];
    if dp.Length > period then
    begin
      i := ((dp.Length - 1) div period) * period;
      while i >= period do
      begin
        dp := dp.Substring(0, i) + sep + dp.Substring(i);
        i := i - period;
      end;
    end;
    ip := ip + '.' + dp;
  end;
  Result := s.Substring(0, startIndex) + s.Substring(startIndex).Replace(s1, ip, []);
end;

var
  i: integer;

begin
  Writeln(commatize(TESTS[0], 0, 2, '*'));
  Writeln(commatize(TESTS[1], 0, 3, '-'));
  Writeln(commatize(TESTS[2], 0, 4, '__'));
  Writeln(commatize(TESTS[3], 0, 5, ' '));
  Writeln(commatize(TESTS[4], 0, 3, '.'));
  for i := 5 to High(TESTS) do
    Writeln(commatize(TESTS[i], 0, 3, ','));
  readln;
end.

Factor

Works with: Factor version 0.99 2020-01-23
USING: accessors grouping io kernel math regexp sequences
splitting strings unicode ;

: numeric ( str -- new-str )
    R/ [1-9][0-9]*/ first-match >string ;

: commas ( numeric-str period separator -- str )
    [ reverse ] [ group ] [ reverse join reverse ] tri* ;

: (commatize) ( text from period separator -- str )
    [ cut dup numeric dup ] 2dip commas replace append ;

: commatize* ( text from period separator -- str )
    reach [ digit? ] any? [ (commatize) ] [ 3drop ] if ;

: commatize ( text -- str ) 0 3 "," commatize* ;

"pi=3.14159265358979323846264338327950288419716939937510582097494459231"
5 5 " " commatize* print
    
"The author has two Z$100000000000000 Zimbabwe notes (100 trillion)."
0 3 "." commatize* print
    
{
    "\"-in Aus$+1411.8millions\""
    "===US$0017440 millions=== (in 2000 dollars)"
    "123.e8000 is pretty big."
    "The land area of the earth is 57268900(29% of the surface) square miles."
    "Ain't no numbers in this here words, nohow, no way, Jose."
    "James was never known as 0000000007"
    "Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe."
    "   $-140000±100 millions."
    "6/9/1946 was a good year for some."
} [ commatize print ] each
Output:
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
"-in Aus$+1,411.8millions"
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.


FreeBASIC

Translation of: VBA
Translation of: Phix
Sub commatize(s As String, sep As String = ",", start As Byte = 1, paso As Byte = 3)
    Dim As Integer l = Len(s)
    For i As Integer = start To l
        If Asc(Mid(s, i, 1)) >= Asc("1") And Asc(Mid(s, i, 1)) <= Asc("9") Then
            For j As Integer =i+1 To l+1
                If j>l Then
                    For k As Integer = j-1-paso To i Step -paso
                        s = Mid(s, 1, k) + sep + Mid(s, k+1, l-k+1)
                        l = Len(s)
                    Next k
                    Exit For
                Else
                    If (Asc(Mid(s, j, 1)) < Asc("0") Or Asc(Mid(s, j, 1)) > Asc("9")) Then
                        For k As Integer = j-1-paso To i Step -paso
                            s = Mid(s, 1, k) + sep + Mid(s, k+1, l-k+1)
                            l = Len(s)
                        Next k
                        Exit For
                    End If                   
                End If
            Next j
            Exit For
        End If
    Next i
    Print s
End Sub

commatize("pi=3.14159265358979323846264338327950288419716939937510582097494459231"," ",6,5)
commatize("The author has two Z$100000000000000 Zimbabwe notes (100 trillion).",".")
commatize("\'-in Aus$+1411.8millions\'",",")
commatize("===US$0017440 millions=== (in 2000 dollars)")
commatize("123.e8000 is pretty big.")
commatize("The land area of the earth is 57268900(29% of the surface) square miles.")
commatize("Ain't no numbers in this here words, nohow, no way, Jose.")
commatize("James was never known as 0000000007")
commatize("Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.")
commatize("   $-140000±100 millions.")
commatize("6/9/1946 was a good year for some.")

Sleep
Output:
Similar a las entradas de VBA o Phix.


FutureBasic

Note: FB throws an error when function parameters are missing, hence the need for placeholders.

local fn commatize( s as Str255, sep as Str255, start as long, stp as long )
  
  if sep[0] == 0 then sep   = ","
  if start  == 0 then start = 1
  if stp    == 0 then stp   = 3
  
  long i, j, k, l = len$(s)
  
  for i = start to l
    if ( asc( mid$( s, i, 1 ) ) >= asc("1") and asc( mid$( s, i, 1) ) <= asc("9") )
      for j = i + 1 to l + 1
        if ( j > l )
          for k = j - 1 - stp to i step -stp
            s = mid$( s, 1, k ) + sep + mid$( s, k + 1, l - k + 1 )
            l = len$(s)
          next k
          exit for
        else
          if ( asc( mid$( s, j, 1 ) ) < asc("0") or asc( mid$( s, j, 1 ) ) > asc("9") )
            for k = j - 1 - stp to i step -stp
              s = mid$( s, 1, k ) + sep + mid$( s, k + 1, l - k + 1 )
              l = len$(s)
            next k
            exit for
          end if
        end if
      next j
      exit for
    end if
  next i
  print s
end fn

window 1

fn commatize("pi=3.14159265358979323846264338327950288419716939937510582097494459231"   , " " , 6, 5 )
fn commatize("The author has two Z$100000000000000 Zimbabwe notes (100 trillion)."      , "." , 0, 0 )
fn commatize("\'-in Aus$+1411.8millions\'"                                              , "," , 0, 0 )
fn commatize("===US$0017440 millions=== (in 2000 dollars)"                              , ""  , 0, 0 )
fn commatize("123.e8000 is pretty big."                                                 , ""  , 0, 0 )
fn commatize("The land area of the earth is 57268900(29% of the surface) square miles." , ""  , 0, 0 )
fn commatize("Ain't no numbers in this here words, nohow, no way, Jose."                , ""  , 0, 0 )
fn commatize("James was never known as 0000000007"                                      , ""  , 0, 0 )
fn commatize("Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.", ",", 0, 0 )
fn commatize("   $-140000±100 millions."                                                , ""  , 0, 0 )
fn commatize("6/9/1946 was a good year for some."                                       , ""  , 0, 0 )

HandleEvents
Output:
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
"-in Aus$+1,411.8millions"
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.


Go

Translation of: Kotlin
package main

import (
    "fmt"
    "regexp"
    "strings"
)

var reg = regexp.MustCompile(`(\.[0-9]+|[1-9]([0-9]+)?(\.[0-9]+)?)`)

func reverse(s string) string {
    r := []rune(s)
    for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
        r[i], r[j] = r[j], r[i]
    }
    return string(r)
}

func commatize(s string, startIndex, period int, sep string) string {
    if startIndex < 0 || startIndex >= len(s) || period < 1 || sep == "" {
        return s
    }
    m := reg.FindString(s[startIndex:]) // this can only contain ASCII characters
    if m == "" {
        return s
    }
    splits := strings.Split(m, ".")
    ip := splits[0]
    if len(ip) > period {
        pi := reverse(ip)
        for i := (len(ip) - 1) / period * period; i >= period; i -= period {
            pi = pi[:i] + sep + pi[i:]
        }
        ip = reverse(pi)
    }
    if strings.Contains(m, ".") {
        dp := splits[1]
        if len(dp) > period {
            for i := (len(dp) - 1) / period * period; i >= period; i -= period {
                dp = dp[:i] + sep + dp[i:]
            }
        }
        ip += "." + dp
    }
    return s[:startIndex] + strings.Replace(s[startIndex:], m, ip, 1)
}

func main() {
    tests := [...]string{
        "123456789.123456789",
        ".123456789",
        "57256.1D-4",
        "pi=3.14159265358979323846264338327950288419716939937510582097494459231",
        "The author has two Z$100000000000000 Zimbabwe notes (100 trillion).",
        "-in Aus$+1411.8millions",
        "===US$0017440 millions=== (in 2000 dollars)",
        "123.e8000 is pretty big.",
        "The land area of the earth is 57268900(29% of the surface) square miles.",
        "Ain't no numbers in this here words, nohow, no way, Jose.",
        "James was never known as 0000000007",
        "Arthur Eddington wrote: I believe there are " +
            "15747724136275002577605653961181555468044717914527116709366231425076185631031296" +
            " protons in the universe.",
        "   $-140000±100 millions.",
        "6/9/1946 was a good year for some.",
    }
    fmt.Println(commatize(tests[0], 0, 2, "*"))
    fmt.Println(commatize(tests[1], 0, 3, "-"))
    fmt.Println(commatize(tests[2], 0, 4, "__"))
    fmt.Println(commatize(tests[3], 0, 5, " "))
    fmt.Println(commatize(tests[4], 0, 3, "."))
    for _, test := range tests[5:] {
        fmt.Println(commatize(test, 0, 3, ","))
    }
}
Output:
1*23*45*67*89.12*34*56*78*9
.123-456-789
5__7256.1D-4
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
-in Aus$+1,411.8millions
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

Haskell

Works with: GHC version 7.8.3
Works with: GHC version 8.6.5
#!/usr/bin/env runhaskell

import Control.Monad (forM_)
import Data.Char (isDigit)
import Data.List (intercalate)
import Data.Maybe (fromMaybe)

{-
I use the suffix "2" in identifiers in place of the more conventional
prime (single quote character), because Rosetta Code's syntax highlighter
still doesn't handle primes in identifiers correctly.
-}

isDigitOrPeriod :: Char -> Bool
isDigitOrPeriod '.' = True
isDigitOrPeriod c = isDigit c

chopUp :: Int -> String -> [String]
chopUp _ [] = []
chopUp by str
  | by < 1 = [str]              -- invalid argument, leave string unchanged
  | otherwise = let (pfx, sfx) = splitAt by str
                in pfx : chopUp by sfx

addSeps :: String -> Char -> Int -> (String -> String) -> String
addSeps str sep by rev =
  let (leading, number) = span (== '0') str
      number2 = rev $ intercalate [sep] $ chopUp by $ rev number
  in leading ++ number2

processNumber :: String -> Char -> Int -> String
processNumber str sep by =
  let (beforeDecimal, rest) = span isDigit str
      (decimal, afterDecimal) = splitAt 1 rest
      beforeDecimal2 = addSeps beforeDecimal sep by reverse
      afterDecimal2 = addSeps afterDecimal sep by id
  in beforeDecimal2 ++ decimal ++ afterDecimal2

commatize2 :: String -> Char -> Int -> String
commatize2 [] _ _ = []
commatize2 str sep by =
  let (pfx, sfx) = break isDigitOrPeriod str
      (number, sfx2) = span isDigitOrPeriod sfx
  in pfx ++ processNumber number sep by ++ sfx2

commatize :: String -> Maybe Char -> Maybe Int -> String
commatize str sep by = commatize2 str (fromMaybe ',' sep) (fromMaybe 3 by)

input :: [(String, Maybe Char, Maybe Int)]
input =
  [ ("pi=3.14159265358979323846264338327950288419716939937510582097494459231", Just ' ', Just 5)
  , ("The author has two Z$100000000000000 Zimbabwe notes (100 trillion).", Just '.', Nothing)
  , ("\"-in Aus$+1411.8millions\"", Nothing, Nothing)
  , ("===US$0017440 millions=== (in 2000 dollars)", Nothing, Nothing)
  , ("123.e8000 is pretty big.", Nothing, Nothing)
  , ("The land area of the earth is 57268900(29% of the surface) square miles.", Nothing, Nothing)
  , ("Ain't no numbers in this here words, nohow, no way, Jose.", Nothing, Nothing)
  , ("James was never known as 0000000007", Nothing, Nothing)
  , ("Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.", Nothing, Nothing)
  , ("   $-140000±100 millions.", Nothing, Nothing)
  , ("6/9/1946 was a good year for some.", Nothing, Nothing)
  ]

main :: IO ()
main =
  forM_ input $ \(str, by, sep) -> do
    putStrLn str
    putStrLn $ commatize str by sep
    putStrLn ""
Output:
pi=3.14159265358979323846264338327950288419716939937510582097494459231
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231

The author has two Z$100000000000000 Zimbabwe notes (100 trillion).
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).

"-in Aus$+1411.8millions"
"-in Aus$+1,411.8millions"

===US$0017440 millions=== (in 2000 dollars)
===US$0017,440 millions=== (in 2000 dollars)

123.e8000 is pretty big.
123.e8000 is pretty big.

The land area of the earth is 57268900(29% of the surface) square miles.
The land area of the earth is 57,268,900(29% of the surface) square miles.

Ain't no numbers in this here words, nohow, no way, Jose.
Ain't no numbers in this here words, nohow, no way, Jose.

James was never known as 0000000007
James was never known as 0000000007

Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.

   $-140000±100 millions.
   $-140,000±100 millions.

6/9/1946 was a good year for some.
6/9/1946 was a good year for some.

J

These rules are relatively baroque, which demands long names and minimally complex statements, thus:

require'regex'
commatize=:3 :0"1 L:1 0
  (i.0) commatize y
:
NB. deal with all those rules about options
  opts=. boxopen x
  char=. (#~ ' '&=@{.@(0&#)@>) opts
  num=. ;opts-.char
  delim=. 0 {:: char,<','
  'begin period'=. _1 0+2{.num,(#num)}.1 3
NB. initialize
  prefix=. begin {.y
  text=. begin }. y
NB. process
  'start len'=. ,'[1-9][0-9]*' rxmatch text
  if.0=len do. y return. end.
  number=. (start,:len) [;.0 text
  numb=. (>:period|<:#number){.number
  fixed=. numb,;delim&,each (-period)<\ (#numb)}.number
  prefix,(start{.text),fixed,(start+len)}.text
)

In use, this might look like:

   (5;5;' ') commatize 'pi=3.14159265358979323846264338327950288419716939937510582097494459231'
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
   '.' commatize 'The author has two Z$100000000000000 Zimbabwe notes (100 trillion).'
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
   commatize '-in Aus$+1411.8millions'
-in Aus$+1,411.8millions
   commatize '===US$0017440 millions=== (in 2000 dollars)'
===US$0017,440 millions=== (in 2000 dollars)
   commatize '123.e8000 is pretty big.'
123.e8000 is pretty big.
   commatize 'The land area of the earth is  57268900(29% of the surface)  square miles.'
The land area of the earth is  57,268,900(29% of the surface)  square miles.
   commatize 'Ain''t no numbers in this here words, nohow, no way, Jose.'
Ain't no numbers in this here words, nohow, no way, Jose.
   commatize 'James was never known as  0000000007'
James was never known as  0000000007
   commatize 'Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.'
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   commatize '   $-140000±100  millions.'
   $-140,000±100  millions.
   commatize '6/9/1946 was a good year for some.'
6/9/1946 was a good year for some.

Java

import java.io.File;
import java.util.*;
import java.util.regex.*;

public class CommatizingNumbers {

    public static void main(String[] args) throws Exception {
        commatize("pi=3.14159265358979323846264338327950288419716939937510582"
                + "097494459231", 6, 5, " ");

        commatize("The author has two Z$100000000000000 Zimbabwe notes (100 "
                + "trillion).", 0, 3, ".");

        try (Scanner sc = new Scanner(new File("input.txt"))) {
            while(sc.hasNext())
                commatize(sc.nextLine());
        }
    }

    static void commatize(String s) {
        commatize(s, 0, 3, ",");
    }

    static void commatize(String s, int start, int step, String ins) {
        if (start < 0 || start > s.length() || step < 1 || step > s.length())
            return;

        Matcher m = Pattern.compile("([1-9][0-9]*)").matcher(s.substring(start));
        StringBuffer result = new StringBuffer(s.substring(0, start));

        if (m.find()) {
            StringBuilder sb = new StringBuilder(m.group(1)).reverse();
            for (int i = step; i < sb.length(); i += step)
                sb.insert(i++, ins);
            m.appendReplacement(result, sb.reverse().toString());
        }

        System.out.println(m.appendTail(result));
    }
}
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
"-in Aus$+1,411.8millions"
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

jq

Adapted from Wren

Works with: jq

Also works with gojq, the Go implementation of jq.

def commatize($s; $start; $step; $sep):

  def isExponent($c): "eEdDpP^∙x↑*⁰¹²³⁴⁵⁶⁷⁸⁹" | index($c);
  def rev: explode|reverse|implode;
  def addSeps($n; $dp):
       { $n, lz: "" }
       | if ($dp|not) and ($n|startswith("0")) and $n != "0"
         then .k = ($n|sub("^0*";""))
         | if (.k == "") then .k = "0" else . end
         | .lz = "0" * (($n|length) - (.k|length))
         | .n = .k
         else .
         end
       | if $dp
         then .n |= rev # reverse if after decimal point
         else .
         end
       | .i = (.n|length) - $step
       | until (.i < 1;
            .n = .n[: .i] + $sep + .n[.i :]
            | .i += - $step )
       | if $dp
         then .n |= rev # reverse again
         else .
         end
       | .lz + .n;

    { acc: $s[:$start],
        n: "",
       dp: false }
    | label $out
    | foreach (range($start; $s|length), null) as $j (.; 
        if $j == null then .emit = true
        else $s[$j:$j+1] as $x
        | ($x | explode[0]) as $c
        | if ($c >= 48 and $c <= 57) 
          then .n += $x
          | if $j == (($s|length)-1)
            then if (.acc != "" and isExponent(.acc[-1:])) 
                 then .acc = $s
                 else .acc += addSeps(.n; .dp)
                 end
            else .
            end
          elif .n != ""
          then if (.acc != "" and isExponent(.acc[-1:]))
               then .acc = $s
               | .emit=true | ., break $out
               elif $x != "."
               then .acc +=  addSeps(.n; .dp) + $s[$j:]
               | .emit=true | ., break $out
               else .acc += addSeps(.n; .dp) + $x
               | .dp = true
               | .n = ""
               end
          else .acc += $x
          end
        end )
   | select(.emit)
   | $s, .acc, ""
;

# Input: the string to be commatized
def commatize:
  commatize(.; 0; 3; ",");

def defaults: [
    "\"-in Aus$+1411.8millions\"",
    "===US$0017440 millions=== (in 2000 dollars)",
    "123.e8000 is pretty big.",
    "The land area of the earth is 57268900(29% of the surface) square miles.",
    "Ain't no numbers in this here words, nohow, no way, Jose.",
    "James was never known as 0000000007",
    "Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.",
    "   $-140000±100 millions.",
    "6/9/1946 was a good year for some."
];

def exercise:
  commatize("123456789.123456789"; 0; 2; "*"),
  commatize(".123456789"; 0; 3; "-"),
  commatize("57256.1D-4"; 0; 4; "__"),
  commatize("pi=3.14159265358979323846264338327950288419716939937510582097494459231"; 0; 5; " "),
  commatize("The author has two Z$100000000000000 Zimbabwe notes (100 trillion)."; 0; 3; "."),

  (defaults[] | commatize) ;

exercise
Output:

Exactly as for Wren.

Julia

Translation of: Perl
input = [
    ["pi=3.14159265358979323846264338327950288419716939937510582097494459231", " ", 5],
    [raw"The author has two Z$100000000000000 Zimbabwe notes (100 trillion).", "."],
    [raw"-in Aus$+1411.8millions"],
    [raw"===US$0017440 millions=== (in 2000 dollars)"],
    ["123.e8000 is pretty big."],
    ["The land area of the earth is  57268900(29% of the surface)  square miles."],
    ["Ain\'t no numbers in this here words, nohow, no way, Jose."],
    ["James was never known as  0000000007"],
    ["Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe."],
    [raw"   $-140000±100  millions."],
    ["6/9/1946 was a good year for some."]]

function commatize(tst)
    grouping = (length(tst) == 3) ? tst[3] : 3
    sep = (length(tst) > 1) ? tst[2] : ","
    rmend(s) = replace(s, Regex("$sep\\Z") =>"")
    greg = Regex(".{$grouping}")
    cins(str) = reverse(rmend(replace(reverse(str), greg => s -> s * sep)))
    mat = match(Regex("(?<![eE\\/])([1-9]\\d{$grouping,})"), tst[1])
    if mat != nothing
        return replace(tst[1], mat.match => cins)
    end
    return tst[1]
end

for tst in input
    println(commatize(tst))
end
Output:

pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231 The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion). -in Aus$+1,411.8millions ===US$0017,440 millions=== (in 2000 dollars) 123.e8000 is pretty big. The land area of the earth is 57,268,900(29% of the surface) square miles. Ain't no numbers in this here words, nohow, no way, Jose. James was never known as 0000000007 Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.

  $-140,000±100  millions.

6/9/1946 was a good year for some.

Kotlin

// version 1.1.4-3

val r = Regex("""(\.[0-9]+|[1-9]([0-9]+)?(\.[0-9]+)?)""")

fun String.commatize(startIndex: Int = 0, period: Int = 3, sep: String = ","): String {
    if ((startIndex !in 0 until this.length) || period < 1 || sep == "") return this
    val m = r.find(this, startIndex)
    if (m == null) return this
    val splits = m.value.split('.')
    var ip = splits[0]
    if (ip.length > period) {       
        val sb = StringBuilder(ip.reversed())
        for (i in (ip.length - 1) / period * period downTo period step period) {
            sb.insert(i, sep)
        }
        ip = sb.toString().reversed()
    }
    if ('.' in m.value) { 
        var dp = splits[1]
        if (dp.length > period) {
            val sb2 = StringBuilder(dp)
            for (i in (dp.length - 1) / period * period downTo period step period) {
                sb2.insert(i, sep)
            }
            dp = sb2.toString()
        }
        ip += "." + dp
    } 
    return this.take(startIndex) + this.drop(startIndex).replaceFirst(m.value, ip)
}

fun main(args: Array<String>) {
    val tests = arrayOf(
        "123456789.123456789",
        ".123456789",
        "57256.1D-4",
        "pi=3.14159265358979323846264338327950288419716939937510582097494459231",
        "The author has two Z$100000000000000 Zimbabwe notes (100 trillion).",
        "-in Aus$+1411.8millions",
        "===US$0017440 millions=== (in 2000 dollars)",
        "123.e8000 is pretty big.",
        "The land area of the earth is 57268900(29% of the surface) square miles.",
        "Ain't no numbers in this here words, nohow, no way, Jose.",
        "James was never known as 0000000007",
        "Arthur Eddington wrote: I believe there are " + 
        "15747724136275002577605653961181555468044717914527116709366231425076185631031296" +     
        " protons in the universe.",
        "   $-140000±100 millions.",
        "6/9/1946 was a good year for some."        
    )

    println(tests[0].commatize(period = 2, sep = "*"))
    println(tests[1].commatize(period = 3, sep = "-"))
    println(tests[2].commatize(period = 4, sep = "__"))   
    println(tests[3].commatize(period = 5, sep = " "))
    println(tests[4].commatize(sep = "."))
    for (test in tests.drop(5)) println(test.commatize())
}
Output:
1*23*45*67*89.12*34*56*78*9
.123-456-789
5__7256.1D-4
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
-in Aus$+1,411.8millions
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

Nim

Translation of: Kotlin

This is a translation of the Kotlin (and Go) algorithm with some modifications.

import re
import strutils

let r = re"(\.[0-9]+|[1-9]([0-9]+)?(\.[0-9]+)?)"

#---------------------------------------------------------------------------------------------------

proc commatize(str: string; startIndex = 0; period = 3; sep = ","): string =

  result = str
  var dp, ip = ""

  if startIndex notin 0..str.high : return

  # Extract first number (if any).
  let (lowBound, highBound) = str.findBounds(r, startIndex)
  if lowBound < 0: return
  let match = str[lowBound..highBound]
  let splits = match.split('.')

  # Process integer part.
  ip = splits[0]
  if ip.len > period:
    var inserted = 0
    for i in countup(ip.high mod period + 1, ip.high, period):
      ip.insert(sep, i + inserted)
      inserted += sep.len

  # Process decimal part.
  if '.' in match:
    dp = splits[1]
    if dp.len > period:
      for i in countdown(dp.high div period * period, period, period):
        dp.insert(sep, i)
    ip &= '.' & dp

  # Replace the number by its "commatized" version.
  result[lowBound..highBound] = ip


#———————————————————————————————————————————————————————————————————————————————————————————————————

const Tests = [
      "123456789.123456789",
      ".123456789",
      "57256.1D-4",
      "pi=3.14159265358979323846264338327950288419716939937510582097494459231",
      "The author has two Z$100000000000000 Zimbabwe notes (100 trillion).",
      "-in Aus$+1411.8millions",
      "===US$0017440 millions=== (in 2000 dollars)",
      "123.e8000 is pretty big.",
      "The land area of the earth is 57268900(29% of the surface) square miles.",
      "Ain't no numbers in this here words, nohow, no way, Jose.",
      "James was never known as 0000000007",
      "Arthur Eddington wrote: I believe there are " &
        "15747724136275002577605653961181555468044717914527116709366231425076185631031296" &
        " protons in the universe.",
      "   $-140000±100 millions.",
      "6/9/1946 was a good year for some."]


echo Tests[0].commatize(period = 2, sep = "*")
echo Tests[1].commatize(period = 3, sep = "-")
echo Tests[2].commatize(period = 4, sep = "__")
echo Tests[3].commatize(period = 5, sep = " ")
echo Tests[4].commatize(sep = ".")

for n in 5..Tests.high:
  echo Tests[n].commatize()
Output:
1*23*45*67*89.12*34*56*78*9
.123-456-789
5__7256.1D-4
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
-in Aus$+1,411.8millions
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

Perl

Displaying before/after only when changes applied.

@input = (
    ['pi=3.14159265358979323846264338327950288419716939937510582097494459231', ' ', 5],
    ['The author has two Z$100000000000000 Zimbabwe notes (100 trillion).', '.'],
    ['-in Aus$+1411.8millions'],
    ['===US$0017440 millions=== (in 2000 dollars)'],
    ['123.e8000 is pretty big.'],
    ['The land area of the earth is  57268900(29% of the surface)  square miles.'],
    ['Ain\'t no numbers in this here words, nohow, no way, Jose.'],
    ['James was never known as  0000000007'],
    ['Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.'],
    ['   $-140000±100  millions.'],
    ['5/9/1946 was a good year for some.']
);

for $i (@input) {
    $old = @$i[0];
    $new = commatize(@$i);
    printf("%s\n%s\n\n", $old, $new) if $old ne $new;
}

sub commatize {
    my($str,$sep,$by) = @_;
    $sep = ',' unless $sep;
    $by  = 3   unless $by;

    $str =~ s/                      # matching rules:
            (?<![eE\/])             #   not following these characters
            ([1-9]\d{$by,})         #   leading non-zero digit, minimum number of digits required
            /c_ins($1,$by,$sep)/ex; # substitute matched text with subroutine output
    return $str;
}

sub c_ins {
    my($s,$by,$sep) = @_;
    ($c = reverse $s) =~ s/(.{$by})/$1$sep/g;
    $c =~ s/$sep$//;
    return reverse $c;
}
Output:
pi=3.14159265358979323846264338327950288419716939937510582097494459231
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231

The author has two Z$100000000000000 Zimbabwe notes (100 trillion).
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).

-in Aus$+1411.8millions
-in Aus$+1,411.8millions

===US$0017440 millions=== (in 2000 dollars)
===US$0017,440 millions=== (in 2000 dollars)

The land area of the earth is  57268900(29% of the surface)  square miles.
The land area of the earth is  57,268,900(29% of the surface)  square miles.

Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.

   $-140000±100  millions.
   $-140,000±100  millions.

Phix

Note that printf() has comma handling built in, for example sprintf("%,d",1234) yields "1,234".
You can find out how that is done by searching for showcommas in builtins\VM\pprntfN.e or (actually JavaScript) in pwa\p2js.js

with javascript_semantics
procedure commatize(string s, sep=",", integer start=1, step=3)
    integer l = length(s)
    for i=start to l do
        if find(s[i],"123456789") then
            for j=i+1 to l+1 do
                if j>l or not find(s[j],"0123456789") then
                    for k=j-1-step to i by -step do
                        s[k+1..k] = sep
                    end for
                    exit
                end if
            end for
            exit
        end if
    end for
    printf(1,"%s\n",{s})
end procedure
 
commatize("pi=3.14159265358979323846264338327950288419716939937510582097494459231"," ",6,5)
commatize("The author has two Z$100000000000000 Zimbabwe notes (100 trillion).",".")
commatize("\"-in Aus$+1411.8millions\"")
commatize("===US$0017440 millions=== (in 2000 dollars)")
commatize("123.e8000 is pretty big.")
commatize("The land area of the earth is 57268900(29% of the surface) square miles.")
commatize("Ain't no numbers in this here words, nohow, no way, Jose.")
commatize("James was never known as 0000000007")
commatize("Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.")
commatize("   $-140000±100 millions.")
commatize("6/9/1946 was a good year for some.")
Output:
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
"-in Aus$+1,411.8millions"
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

Python

import re as RegEx


def Commatize( _string, _startPos=0, _periodLen=3, _separator="," ):
	outString = ""
	strPos = 0
	matches = RegEx.findall( "[0-9]*", _string )

	for match in matches[:-1]:
		if not match:
			outString += _string[ strPos ]
			strPos += 1
		else:
			if len(match) > _periodLen:
				leadIn = match[:_startPos]
				periods =  [ match [ i:i + _periodLen ] for i in range ( _startPos, len ( match ), _periodLen ) ]
				outString += leadIn + _separator.join( periods )
			else:
				outString += match

			strPos += len( match )

	return outString



print ( Commatize( "pi=3.14159265358979323846264338327950288419716939937510582097494459231", 0, 5, " " ) )
print ( Commatize( "The author has two Z$100000000000000 Zimbabwe notes (100 trillion).", 0, 3, "." ))
print ( Commatize( "\"-in Aus$+1411.8millions\"" ))
print ( Commatize( "===US$0017440 millions=== (in 2000 dollars)" ))
print ( Commatize( "123.e8000 is pretty big." ))
print ( Commatize( "The land area of the earth is 57268900(29% of the surface) square miles." ))
print ( Commatize( "Ain't no numbers in this here words, nohow, no way, Jose." ))
print ( Commatize( "James was never known as 0000000007" ))
print ( Commatize( "Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe." ))
print ( Commatize( "␢␢␢$-140000±100 millions." ))
print ( Commatize( "6/9/1946 was a good year for some." ))

Racket

Note that the post-number part of the split catches the date in 6/9/1946 (I think that's desirable) but 1946-09-06 would still be commatized as 1,946-09-06 -- it would take date recognition to capture that case.

All tests pass (so it's as good as Perl, I guess).

#lang racket
(require (only-in srfi/13 [string-reverse gnirts]))

;; returns a string with the "comma"s inserted every step characters from the RIGHT of n.
;; because of the right handedness of this, there is a lot of reversal going on
(define ((insert-commas comma step) n)
  (define px (pregexp (format ".{1,~a}" step)))
  (string-join (add-between (reverse (map gnirts (regexp-match* px (gnirts n)))) comma) ""))

(define (commatize s #:start (start 0) #:comma (comma ",") #:step (step 3))
  (define ins-comms (insert-commas comma step)) ; specific to our comma and step
  
  (define split-into-numbers
    (match-lambda
      [(regexp
        #px"^([^1-9]*)([1-9][0-9.]*)(\\S*)(.*)$" ; see below for description of bits
        (list _                               ; the whole match
              (app split-into-numbers pre)    ; recur on left
              num                             ; the number bit before any exponent or other
                                              ; interestingness
              post-number                     ; from exponent to the first space
              (app split-into-numbers post))) ; recur on right
       (define skip (substring num 0 start))
       (match-define
         (regexp #px"^(.*?)(\\..*)?$"
                 (list _                      ; whole match
                       (app ins-comms n)      ; the bit that gets the commas added
                       (or (? string? d)      ; if it matches, then the raw string is in d
                           (and #f (app (lambda (f) "") d))))) ; if (...)? doesn't match it returns
                                                               ; #f which we thunk to an empty string
         (substring num start))                       ; do the match on the unskipped bit
       (string-append pre skip n d post-number post)] ; stitch it back together
      [else else]))                                   ; if it doesn't match leave as is
  
  ;; kick it off
  (split-into-numbers s))

(module+ test
  (require tests/eli-tester)
  
  (test
   (commatize "pi=3.14159265358979323846264338327950288419716939937510582097494459231"
              #:start 6 #:comma " " #:step 5)
   =>"pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231"
   
   (commatize "The author has two Z$100000000000000 Zimbabwe notes (100 trillion)." #:comma ".")
   =>"The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion)."
   
   (commatize "-in Aus$+1411.8millions")
   =>"-in Aus$+1,411.8millions"
   
   (commatize "===US$0017440 millions=== (in 2000 dollars)")
   =>"===US$0017,440 millions=== (in 2,000 dollars)"
   
   (commatize "123.e8000 is pretty big.")
   =>"123.e8000 is pretty big."
   
   (commatize "The land area of the earth is  57268900(29% of the surface)  square miles.")
   =>"The land area of the earth is  57,268,900(29% of the surface)  square miles."
   
   (commatize "Ain't no numbers in this here words, nohow, no way, Jose.")
   =>"Ain't no numbers in this here words, nohow, no way, Jose."
   
   (commatize "James was never known as  0000000007")
   =>"James was never known as  0000000007"
   
   (commatize "Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.")
   =>"Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe."
   
   (commatize "   $-140000±100  millions.")
   
   =>"   $-140,000±100  millions."
   (commatize "6/9/1946 was a good year for some.")
   =>"6/9/1946 was a good year for some."))

Raku

(formerly Perl 6)

for ('pi=3.14159265358979323846264338327950288419716939937510582097494459231', {:6at, :5by, :ins(' ')}),
    ('The author has two Z$100000000000000 Zimbabwe notes (100 trillion).', {:ins<.>}),
    '-in Aus$+1411.8millions',
    '===US$0017440 millions=== (in 2000 dollars)',
    '123.e8000 is pretty big.',
    'The land area of the earth is  57268900(29% of the surface)  square miles.',
    'Ain\'t no numbers in this here words, nohow, no way, Jose.',
    'James was never known as  0000000007',
    'Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.',
    '   $-140000±100  millions.',
    '6/9/1946 was a good year for some.'
    {
        say "Before: ", .[0];
        say " After: ", .[1] ?? .[0].&commatize( |.[1] ) !! .&commatize;
    }
 
sub commatize($s, :$at = 0, :$ins = ',', :$by = 3) {
    $s.subst: :continue($at), :1st, / <[1..9]> <[0..9]>* /,
        *.flip.comb(/<{ ".**1..$by" }>/).join($ins).flip;
}
Output:
Before: pi=3.14159265358979323846264338327950288419716939937510582097494459231
 After: pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
Before: The author has two Z$100000000000000 Zimbabwe notes (100 trillion).
 After: The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
Before: -in Aus$+1411.8millions
 After: -in Aus$+1,411.8millions
Before: ===US$0017440 millions=== (in 2000 dollars)
 After: ===US$0017,440 millions=== (in 2000 dollars)
Before: 123.e8000 is pretty big.
 After: 123.e8000 is pretty big.
Before: The land area of the earth is  57268900(29% of the surface)  square miles.
 After: The land area of the earth is  57,268,900(29% of the surface)  square miles.
Before: Ain't no numbers in this here words, nohow, no way, Jose.
 After: Ain't no numbers in this here words, nohow, no way, Jose.
Before: James was never known as  0000000007
 After: James was never known as  0000000007
Before: Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.
 After: Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
Before:    $-140000±100  millions.
 After:    $-140,000±100  millions.
Before: 6/9/1946 was a good year for some.
 After: 6/9/1946 was a good year for some.

REXX

The hardest part of the   comma   function is to locate where a   usable   number starts and ends.

/*REXX program adds commas (or other chars) to a  string  or a  number  within a string.*/
@. =
@.1= "pi=3.14159265358979323846264338327950288419716939937510582097494459231"
@.2= "The author has two Z$100000000000000 Zimbabwe notes (100 trillion)."
@.3= "-in Aus$+1411.8millions"
@.4= "===US$0017440 millions=== (in 2000 dollars)"
@.5= "123.e8000 is pretty big."
@.6= "The land area of the earth is  57268900(29% of the surface)  square miles."
@.7= "Ain't no numbers in this here words, nohow, no way, Jose."
@.8= "James was never known as  0000000007"
@.9= "Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe."
@.10= "   $-140000±100  millions."
@.11= "6/9/1946 was a good year for some."

       do i=1  while  @.i\=='';               if i\==1  then say  /*process each string.*/
                     say 'before──►'@.i                           /*show the before str.*/
       if i==1  then say ' after──►'comma(@.i, 'blank', 5, , 6)   /*   p=5,  start=6.   */
       if i==2  then say ' after──►'comma(@.i, ".")               /*comma=decimal point.*/
       if i>2   then say ' after──►'comma(@.i)                    /*use the defaults.   */
       end   /*j*/
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
comma: procedure; parse arg x,sep,period,times,start       /*obtain true case arguments.*/
                        arg  ,sepU                         /*   "   uppercase 2nd arg.  */
       bla= ' '                                            /*literal to hold a  "blank".*/
       sep= word(sep ',', 1)                               /*define comma (string/char.)*/
       if sepU=='BLANK'  then sep= bla                     /*allow the use of  'BLANK'. */
       period= word(period 3, 1)                           /*defined "period" to be used*/
       times= word(times 999999999, 1)                     /*limits # changes to be made*/
       start= word(start 1        , 1)                     /*where to start commatizing.*/
                                                           /* [↓]  various error tests. */
       if \datatype(period, 'W')  | ,                      /*test for a whole number.   */
          \datatype(times , 'W')  | ,                      /*  "   "  "   "      "      */
          \datatype(start , 'W')  | ,                      /*  "   "  "   "      "      */
          start <1                | ,                      /*start can't be less then 1.*/
          arg() >5                      then return x      /*# of args can't be  >  5.  */
                                                           /* [↑]  some arg is invalid. */
       op= period                                          /*save the original period.  */
       period= abs(period)                                 /*use the absolute value.    */
       n= x'.9'                                            /*a literal string for  end. */
       digs=   123456789                                   /*the legal digits for start.*/
       digsz=  1234567890                                  /* "    "      "    "   fin. */
       digszp= 1234567890.                                 /* "    "      "    "   fin. */
                                                           /* [↓]  note: no zero in digs*/
       if op<0  then do                                    /*Negative?  Treat as chars. */
                     beg= start                            /*begin at the start.        */
                     L= length(x)                          /*obtain the length of  X.   */
                     fin= L - verify( reverse(x), bla) + 1 /*find the ending of the num.*/
                     end                                   /* [↑]  find number ending.  */
                else do                                    /*Positive?  Treat as numbers*/
                     beg=   verify(n, digs,  "M",start)    /*find beginning of number.  */
                     v2=max(verify(n, digszp,'M',start),1) /*end of the usable number.  */
                     fin=verify(n, digsz, , v2) -period -1 /*adjust the ending  (fin).  */
                     end                                   /* [↑]  find ending of number*/
       #= 0                                                /*the count of changes made. */
       if beg>0  &  fin>0  then                            /* [↓]  process  TIMES  times*/
                     do j=fin   to beg   by -period   while #<times
                     x= insert(sep, x, j)                  /*insert a comma into string.*/
                     #= # + 1                              /*bump the count of changes. */
                     end   /*j*/                           /*(maybe no changes are made)*/
       return x                                            /*return the commatized str. */
output   when using the internal default inputs:
before──►pi=3.14159265358979323846264338327950288419716939937510582097494459231
 after──►pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231

before──►The author has two Z$100000000000000 Zimbabwe notes (100 trillion).
 after──►The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).

before──►-in Aus$+1411.8millions
 after──►-in Aus$+1,411.8millions

before──►===US$0017440 millions=== (in 2000 dollars)
 after──►===US$0017,440 millions=== (in 2000 dollars)

before──►123.e8000 is pretty big.
 after──►123.e8000 is pretty big.

before──►The land area of the earth is  57268900(29% of the surface)  square miles.
 after──►The land area of the earth is  57,268,900(29% of the surface)  square miles.

before──►Ain't no numbers in this here words, nohow, no way, Jose.
 after──►Ain't no numbers in this here words, nohow, no way, Jose.

before──►James was never known as  0000000007
 after──►James was never known as  0000000007

before──►Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.
 after──►Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.

before──►   $-140000±100  millions.
 after──►   $-140,000±100  millions.

before──►6/9/1946 was a good year for some.
 after──►6/9/1946 was a good year for some.

Scala

Java-ish version

import java.io.File
import java.util.Scanner
import java.util.regex.Pattern

object CommatizingNumbers extends App {

  def commatize(s: String): Unit = commatize(s, 0, 3, ",")

  def commatize(s: String, start: Int, step: Int, ins: String): Unit = {
    if (start >= 0 && start <= s.length && step >= 1 && step <= s.length) {
      val m = Pattern.compile("([1-9][0-9]*)").matcher(s.substring(start))
      val result = new StringBuffer(s.substring(0, start))
      if (m.find) {
        val sb = new StringBuilder(m.group(1)).reverse
        for (i <- step until sb.length by step) sb.insert(i, ins)
        m.appendReplacement(result, sb.reverse.toString)
      }
      println(m.appendTail(result))
    }
  }

  commatize("pi=3.14159265358979323846264338327950288419716939937510582" + "097494459231", 6, 5, " ")
  commatize("The author has two Z$100000000000000 Zimbabwe notes (100 " + "trillion).", 0, 3, ".")

  val sc = new Scanner(new File("input.txt"))
  while (sc.hasNext) commatize(sc.nextLine)
}

Swift

Translation of: Kotlin
import Foundation

extension String {
  private static let commaReg = try! NSRegularExpression(pattern: "(\\.[0-9]+|[1-9]([0-9]+)?(\\.[0-9]+)?)")

  public func commatize(start: Int = 0, period: Int = 3, separator: String = ",") -> String {
    guard separator != "" else {
      return self
    }

    let sep = Array(separator)
    let startIdx = index(startIndex, offsetBy: start)
    let matches = String.commaReg.matches(in: self, range: NSRange(startIdx..., in: self))

    guard !matches.isEmpty else {
      return self
    }

    let fullMatch = String(self[Range(matches.first!.range(at: 0), in: self)!])
    let splits = fullMatch.components(separatedBy: ".")
    var ip = splits[0]

    if ip.count > period {
      var builder = Array(ip.reversed())

      for i in stride(from: (ip.count - 1) / period * period, through: period, by: -period) {
        builder.insert(contentsOf: sep, at: i)
      }

      ip = String(builder.reversed())
    }

    if fullMatch.contains(".") {
      var dp = splits[1]

      if dp.count > period {
        var builder = Array(dp)

        for i in stride(from: (dp.count - 1) / period * period, through: period, by: -period) {
          builder.insert(contentsOf: sep, at: i)
        }

        dp = String(builder)
      }

      ip += "." + dp
    }

    return String(prefix(start)) + String(dropFirst(start)).replacingOccurrences(of: fullMatch, with: ip)
  }
}

let tests = [
  "123456789.123456789",
  ".123456789",
  "57256.1D-4",
  "pi=3.14159265358979323846264338327950288419716939937510582097494459231",
  "The author has two Z$100000000000000 Zimbabwe notes (100 trillion).",
  "-in Aus$+1411.8millions",
  "===US$0017440 millions=== (in 2000 dollars)",
  "123.e8000 is pretty big.",
  "The land area of the earth is 57268900(29% of the surface) square miles.",
  "Ain't no numbers in this here words, nohow, no way, Jose.",
  "James was never known as 0000000007",
  "Arthur Eddington wrote: I believe there are " +
      "15747724136275002577605653961181555468044717914527116709366231425076185631031296" +
      " protons in the universe.",
  "   $-140000±100 millions.",
  "6/9/1946 was a good year for some."
]

print(tests[0].commatize(period: 2, separator: "*"))
print(tests[1].commatize(period: 3, separator: "-"))
print(tests[2].commatize(period: 4, separator: "__"))
print(tests[3].commatize(period: 5, separator: " "))
print(tests[4].commatize(separator: "."))

for testCase in tests.dropFirst(5) {
  print(testCase.commatize())
}
Output:
1*23*45*67*89.12*34*56*78*9
.123-456-789
5__7256.1D-4
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
-in Aus$+1,411.8millions
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

VBA

Translation of: Phix
Public Sub commatize(s As String, Optional sep As String = ",", Optional start As Integer = 1, Optional step As Integer = 3)
    Dim l As Integer: l = Len(s)
        For i = start To l
            If Asc(Mid(s, i, 1)) >= Asc("1") And Asc(Mid(s, i, 1)) <= Asc("9") Then
                For j = i + 1 To l + 1
                    If j > l Then
                        For k = j - 1 - step To i Step -step
                            s = Mid(s, 1, k) & sep & Mid(s, k + 1, l - k + 1)
                            l = Len(s)
                        Next k
                        Exit For
                    Else
                        If (Asc(Mid(s, j, 1)) < Asc("0") Or Asc(Mid(s, j, 1)) > Asc("9")) Then
                            For k = j - 1 - step To i Step -step
                                s = Mid(s, 1, k) & sep & Mid(s, k + 1, l - k + 1)
                                l = Len(s)
                            Next k
                            Exit For
                        End If
                    End If
                Next j
                Exit For
            End If
        Next i
        Debug.Print s
    End Sub
Public Sub main()
    commatize "pi=3.14159265358979323846264338327950288419716939937510582097494459231", " ", 6, 5
    commatize "The author has two Z$100000000000000 Zimbabwe notes (100 trillion).", "."
    commatize """-in Aus$+1411.8millions"""
    commatize "===US$0017440 millions=== (in 2000 dollars)"
    commatize "123.e8000 is pretty big."
    commatize "The land area of the earth is 57268900(29% of the surface) square miles."
    commatize "Ain't no numbers in this here words, nohow, no way, Jose."
    commatize "James was never known as 0000000007"
    commatize "Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe."
    commatize "   $-140000±100 millions."
    commatize "6/9/1946 was a good year for some."
End Sub
Output:
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
"-in Aus$+1,411.8millions"
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

VBScript

Adapted from the Future Basic code

function commatize( s , sep, start , stp  )
  
  if sep ="" then sep   = ","
  if start  ="" then start = 1
  if stp    ="" then stp   = 3
  
 Dim i, j, k, l
 l = len(s)
  
  for i = start to l
    if ( asc( mid( s, i, 1 ) ) >= asc("1") and asc( mid( s, i, 1) ) <= asc("9") ) then
      for j = i + 1 to l + 1
        if ( j > l ) then
          for k = j - 1 - stp to i step -stp
            s = mid( s, 1, k ) + sep + mid( s, k + 1, l - k + 1 )
            l = len(s)
          next 'k
          exit for
        else
          if ( asc( mid( s, j, 1 ) ) < asc("0") or asc( mid( s, j, 1 ) ) > asc("9") ) then
            for k = j - 1 - stp to i step -stp
              s = mid( s, 1, k ) + sep + mid( s, k + 1, l - k + 1 )
              l = len(s)
            Next ' k
            exit for
          end if
        end if
      next 'j
      exit for
    end if
  next '
  commatize=S
end function

wscript.echo commatize("pi=3.14159265358979323846264338327950288419716939937510582097494459231"   , " " , 6, 5 )
wscript.echo commatize("The author has two Z$100000000000000 Zimbabwe notes (100 trillion)."      , "." , "", "" )
wscript.echo commatize("\'-in Aus$+1411.8millions\'"                                              , "," , "", "" )
wscript.echo commatize("===US$0017440 millions=== (in 2000 dollars)"                              , ""  , "", "" )
wscript.echo commatize("123.e8000 is pretty big."                                                 , ""  , "", "" )
wscript.echo commatize("The land area of the earth is 57268900(29% of the surface) square miles." , ""  , "", "" )
wscript.echo commatize("Ain't no numbers in this here words, nohow, no way, Jose."                , ""  , "", "" )
wscript.echo commatize("James was never known as 0000000007"                                      , ""  , "", "" )
wscript.echo commatize("Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.", ",", "", "" )
wscript.echo commatize("   $-140000±100 millions."                                                , ""  , "", "" )
wscript.echo commatize("6/9/1946 was a good year for some."                                       , ""  , "", "" )
Output:

pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).
\'-in Aus$+1,411.8millions\'
===US$0017,440 millions=== (in 2000 dollars)
123.e8000 is pretty big.
The land area of the earth is 57,268,900(29% of the surface) square miles.
Ain't no numbers in this here words, nohow, no way, Jose.
James was never known as 0000000007
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.
   $-140,000±100 millions.
6/9/1946 was a good year for some.

Wren

var commatize = Fn.new { |s, start, step, sep|
    var addSeps = Fn.new { |n, dp|
        var lz = ""
        if (!dp && n.startsWith("0") && n != "0") {
            var k = n.trimStart("0")
            if (k == "") k = "0"
            lz = "0" * (n.count - k.count)
            n = k
        }
        if (dp) n = n[-1..0] // invert if after decimal point
        var i = n.count - step
        while (i >= 1) {
            n = n[0...i] + sep + n[i..-1]
            i = i - step
        }
        if (dp) n = n[-1..0] // invert back
        return lz + n
    }

    var t = s.toList
    var isExponent = Fn.new { |c| "eEdDpP^∙x↑*⁰¹²³⁴⁵⁶⁷⁸⁹".contains(c) }
    var acc = (start == 0) ? "" : t.toList[0...start].join()
    var n = ""
    var dp = false
    for (j in start...t.count) {
        var c = t[j].codePoints[0]
        if (c >= 48 && c <= 57) {
            n = n + t[j]
            if (j == t.count-1) {
                if (acc != "" && isExponent.call(acc[-1])) {
                    acc = s
                } else {
                    acc = acc + addSeps.call(n, dp)
                }
            }
        } else if (n != "") {
            if (acc != "" && isExponent.call(acc[-1])) {
                acc = s
                break
            } else if (t[j] != ".") {
                acc = acc + addSeps.call(n, dp) + t[j..-1].join()
                break
            } else {
                acc = acc + addSeps.call(n, dp) + t[j]
                dp = true
                n = ""
            } 
        } else {
            acc = acc + t[j]
        }
    }

    System.print(s)
    System.print(acc)
    System.print()
}

// special version of the above which uses defaults for start, step and sep.
var commatize2 = Fn.new { |s| commatize.call(s, 0, 3, ",") }

commatize.call("123456789.123456789", 0, 2, "*")
commatize.call(".123456789", 0, 3, "-")
commatize.call("57256.1D-4", 0, 4, "__")
commatize.call("pi=3.14159265358979323846264338327950288419716939937510582097494459231", 0, 5, " ")
commatize.call("The author has two Z$100000000000000 Zimbabwe notes (100 trillion).", 0, 3, ".")

var defaults = [
    "\"-in Aus$+1411.8millions\"",
    "===US$0017440 millions=== (in 2000 dollars)",
    "123.e8000 is pretty big.",
    "The land area of the earth is 57268900(29\% of the surface) square miles.",
    "Ain't no numbers in this here words, nohow, no way, Jose.",
    "James was never known as 0000000007",
    "Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.",
    "   $-140000±100 millions.",
    "6/9/1946 was a good year for some."
]

defaults.each { |d| commatize2.call(d) }
Output:
123456789.123456789
1*23*45*67*89.12*34*56*78*9

.123456789
.123-456-789

57256.1D-4
5__7256.1D-4

pi=3.14159265358979323846264338327950288419716939937510582097494459231
pi=3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59231

The author has two Z$100000000000000 Zimbabwe notes (100 trillion).
The author has two Z$100.000.000.000.000 Zimbabwe notes (100 trillion).

"-in Aus$+1411.8millions"
"-in Aus$+1,411.8millions"

===US$0017440 millions=== (in 2000 dollars)
===US$0017,440 millions=== (in 2000 dollars)

123.e8000 is pretty big.
123.e8000 is pretty big.

The land area of the earth is 57268900(29% of the surface) square miles.
The land area of the earth is 57,268,900(29% of the surface) square miles.

Ain't no numbers in this here words, nohow, no way, Jose.
Ain't no numbers in this here words, nohow, no way, Jose.

James was never known as 0000000007
James was never known as 0000000007

Arthur Eddington wrote: I believe there are 15747724136275002577605653961181555468044717914527116709366231425076185631031296 protons in the universe.
Arthur Eddington wrote: I believe there are 15,747,724,136,275,002,577,605,653,961,181,555,468,044,717,914,527,116,709,366,231,425,076,185,631,031,296 protons in the universe.

   $-140000±100 millions.
   $-140,000±100 millions.

6/9/1946 was a good year for some.
6/9/1946 was a good year for some.