I have a number 12343423455.23353. I want to format the number with thousand separator. So th output would be 12,343,423,455.23353
The character used as the thousands separatorIn the United States, this character is a comma (,). In Germany, it is a period (.). Thus one thousand and twenty-five is displayed as 1,025 in the United States and 1.025 in Germany. In Sweden, the thousands separator is a space.
The decimal separator is also called the radix character. Likewise, while the U.K. and U.S. use a comma to separate groups of thousands, many other countries use a period instead, and some countries separate thousands groups with a thin space.
A decimal separator is a symbol used to separate the integer part from the fractional part of a number written in decimal form (e.g., "." in 12.45). Different countries officially designate different symbols for use as the separator.
$ printf "%'.3f\n" 12345678.901 12,345,678.901
tl;dr
Use numfmt, if GNU utilities are available, such as on Linux by default:
numfmt --grouping 12343423455.23353 # -> 12,343,423,455.23353 in locale en_USOtherwise, use printf with the ' field flag wrapped in a shell function that preserves the number of input decimal places (does not hard-code the number of output decimal places).
groupDigits 12343423455.23353 # -> 12,343,423,455.23353 in locale en_USgroupDigits(), which also supports multiple input numbers.Ad-hoc alternatives involving subshells that also preserve the number of input decimal places (assumes that the input decimal mark is either . or ,):
(n=$(</dev/stdin); f=${n#*[.,]}; printf "%'.${#f}f\n" "$n") <<<12343423455.23353 $n: n=12343423455.23353; (f=${n#*[.,]} printf "%'.${#f}f\n" "$n") Alternatively, consider use of my Linux/macOS grp CLI (installable with npm install -g grp-cli):
grp -n 12343423455.23353In all cases there are caveats; see below.
Ignacio Vazquez-Abrams's answer contains the crucial pointer for use with printf: the ' field flag (following the %) formats a number with the active locale's thousand separator:
man printf (man 1 printf) does not contain this information itself: the utility / shell builtin printf ultimately calls the library function printf(), and only man 3 printf gives the full picture with respect to supported formats.LC_NUMERIC and, indirectly, LANG or LC_ALL control the active locale with respect to number formatting.numfmt and printf respect the active locale, both with respect to the thousands separator and the decimal mark ("decimal point").printf by itself, as in Ignacio's answer, requires that you hard-code the number of output decimal places, rather than preserving however many decimal places the input has; it is this limitation that groupDigits() below overcomes.printf "%'.<numDecPlaces>f" does have one advantage over numfmt --grouping, however: numfmt only accepts decimal numbers, whereas printf's %f also accepts hexadecimal integers (e.g., 0x3e8) and numbers in decimal scientific notation (e.g., 1e3).Locales without grouping: Some locales, notably C and POSIX, by definition do NOT apply grouping, so use of ' has no effect in that event.
Real-world locale inconsistencies across platforms:
(LC_ALL='de_DE.UTF-8'; printf "%'.1f\n" 1000) # SHOULD yield: 1.000,01.000,0, as expected.1000,0 - NO grouping(!).numfmt or printf, it: (LC_ALL='lt_LT.UTF-8'; printf "%'.1f\n" 1000,1) # -> '1 000,1'Portability: POSIX doesn't require the printf utility (as opposed to the C printf() library function) to support floating-point format characters such as %f, given that POSIX[-like] shells are integer-only; in practice, however, I'm not aware of any shells/platforms that do not.
Rounding errors and overflow:
numfmt and printf as described, round-trip conversion occurs (string -> number -> string), which is subject to rounding errors; in other words: reformatting with digit grouping can lead to a different number.f to employ IEEE-754 double-precision floating-point values, only up to 15 significant digits (digits irrespective of the location of the decimal mark) are guaranteed to be accurately preserved (though for specific numbers it may work with more digits). In practice, numfmt and GNU printf can accurately handle more than that; see below. If anyone knows how and why, let me know. numfmt and printf in general, and between printf implementations across platforms; for example:numft:
[Fixed in coreutils 8.24, according to @pixelbeat] Starting with 20 significant digits, the value overflows quietly(!) - presumably a bug (as of GNU coreutils 8.23):
# 20 significant digits cause quiet overflow: $ (fractPart=0000000000567890; num="1000.${fractPart}"; numfmt --grouping "$num") -92.23372036854775807 # QUIET OVERFLOW By contrast, a number that is too large does generate an error by default.
printf:
Linux printf handles up to 20 significant digits accurately, whereas the BSD/macOS implementation is limited to 17:
# Linux: 21 significant digits cause rounding error: $ (fractPart=00000000005678901; num="1000.${fractPart}"; printf "%'.${#fractPart}f\n" "$num") 1,000.00000000005678902 # ROUNDING ERROR # BSD/macOS: 18 significant digits cause rounding error: $ (fractPart=00000000005678; num="1000.${fractPart}"; printf "%'.${#fractPart}f\n" "$num") 1,000.00000000005673 # ROUNDING ERROR The Linux version never seems to overflow, whereas the BSD/macOS version reports an error with numbers that are too large.
groupDigits():# SYNOPSIS # groupDigits num ... # DESCRIPTION # Formats the specified number(s) according to the rules of the # current locale in terms of digit grouping (thousands separators). # Note that input numbers # - must not already be digit-grouped themselves, # - must use the *current* locale's decimal mark. # Numbers can be integers or floats. # Processing stops at the first number that can't be formatted, and a # non-zero exit code is returned. # CAVEATS # - No input validation is performed. # - printf(1) is not guaranteed to support non-integer formats by POSIX, # though not doing so is rare these days. # - Round-trip number conversion is involved (string > double > string) # so rounding errors can occur. # EXAMPLES # groupDigits 1000 # -> '1,000' # groupDigits 1000.5 # -> '1,000.5' # (LC_ALL=lt_LT.UTF-8; groupDigits 1000,5) # -> '1 000,5' groupDigits() { local decimalMark fractPart decimalMark=$(printf "%.1f" 0); decimalMark=${decimalMark:1:1} for num; do fractPart=${num##*${decimalMark}}; [[ "$num" == "$fractPart" ]] && fractPart='' printf "%'.${#fractPart}f\n" "$num" || return done }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With