commas added to numbers

What
commas added to numbers
Where
From the contact
Description
Tiny Tcl regsub comma to add commas to numbers, taking leading white space, - or +, and decimal points into account.
Updated
?
Contact
mailto:[email protected] (John Allen)

In Bag of algorithms there's a one-liner alternative by Peter Spjuth (in the Tcl chatroom, 2004-10-05) using modern regexp features:

 proc commify number {regsub -all {\d(?=(\d{3})+($|\.))} $number {\0,}}

e.g.:

  % commify 1000000.00
  1,000,000.00

Comment: commify only works for numbers with less than 3 digits after decimal point e.g.

 () 4 % commify 1234567.123456
 1,234,567.123,456

AMG: I added some code to SYStems's page that puts _'s in numbers in the same place commas would go. There's both a [for] version and a [regsub] version. It's not possible (I don't think) to make a one-liner [regsub] that correctly places commas after the decimal point since such a feature would require "look-behind" patterns, so I reverse the string, [regsub], then reverse again.


KPV The following is a tcl translation of a perl code snippet to add commas to a number. It's not quite a one-liner but pretty close.

proc commify { num {sep ,} } {
    while {[regsub {^([-+]?\d+)(\d\d\d)} $num "\\1$sep\\2" num]} {}
    return $num
}

If you want groupings other than 3 try:

proc commify { num {sep ,} {groupSize 3}} {
    while {[regsub "^(\[-+]?\\d+)(\\d{$groupSize})" $num "\\1$sep\\2" num]} {}
    return $num
}

aspect: This implementation is a case of iterating a command on a value until it reaches a "fixed point" (the result equals the input), and then stopping. We can capture that operation by writing a proc:

# this variation takes its argument through [upvar] and sets it to the result of $script
proc fixpoint {varName script} {
    upvar 1 $varName arg
    while {[set res [uplevel 1 $script]] ne $arg} {
        set arg $res
    }
    return $arg
}

.. which we then use like this:

proc commify {num {sep ,}} {
    fixpoint num {
        regsub {^([-+]?\d+)(\d\d\d)} $num "\\1$sep\\2"
    }
}

WJP See Delimiting Numbers for a procedure that can handle groups of size other than three (such as the groups of size four used in some Asian languages).


jbr The one liner is the combination of the original code above and the explicit selection of the trailing decimals:

 proc commify number {regsub -all \\d(?=(\\d{3})+([regexp -inline {\.\d*$} $number]$)) $number {\0,}}

Getting a little wide but fully parameterized:

 proc commify { n { s , } { g 3 } } { regsub -all \\d(?=(\\d{$g})+([regexp -inline {\.\d*$} $n]$)) $n \\0$s }