How to format a number using comma as thousands separator in C?
Asked Answered
P

27

108

In C, how can I format a large number from e.g. 1123456789 to 1,123,456,789?

I tried using printf("%'10d\n", 1123456789), but that doesn't work.

Could you advise anything? The simpler the solution the better.

Philippines answered 19/9, 2009 at 23:38 Comment(7)
Just an FYI: the 'thousands separator' flag for the printf() family of formatted IO functions (the single-quote character: ') is a non-standard flag that's supported only in a few library implementations. It's too bad that it's not standard.Evy
It's locale-dependent. According to the Linux man page, it looks at LC_NUMERIC. However, I don't know what locale supports this.Willettawillette
@Joey, setting the LC_NUMERIC locale to the current "" makes the ' work on my Mac and on a linux machine I just checked.Luana
Note that the POSIX 2008 (2013) versions of the printf() family of functions does standardize the use of the ' (single quote or apostrophe) character with the decimal number formatting conversion specifications to specify that the number should be formatted with thousands separators.Gastrostomy
Also note that in the default "C" locale, the non-monetary thousands separator is undefined, so the "%'d" won't produce commas in the "C" locale. You need to set a locale with an appropriate non-monetary thousand separator. Often, setlocale(LC_ALL, ""); will do the job — other values for the locale name (other than the empty string) are implementation defined.Gastrostomy
Looking at all the solutions given here... it is quite amazing that such a basic formatting feature is not (and has not become) a standard part of the C library. Probably an almost zero effort when added to the printf implementation.Carnarvon
I believe locales for India should use 3 digits for the least significant group, and thereafter 2 digits per group — encoded in the struct lconv elements for grouping as "\3\2\177" if plain char is signed, or "\3\2\377" if plain char is unsigned. A lakh is 1,00,000 and a crore is 1,00,00,000. See also Wikipedia on Lakh and Crore.Gastrostomy
L
108

If your printf supports the ' flag (as required by POSIX 2008 printf()), you can probably do it just by setting your locale appropriately. Example:

#include <stdio.h>
#include <locale.h>

int main(void)
{
    setlocale(LC_NUMERIC, "");
    printf("%'d\n", 1123456789);
    return 0;
}

And build & run:

$ ./example 
1,123,456,789

Tested on Mac OS X & Linux (Ubuntu 10.10).

Luana answered 27/7, 2012 at 20:31 Comment(10)
I've tested this on sprintf() in an embedded system and it doesn't work (obviously, because as you say, it won't support the ' flag.Anderer
I'm sure you can find a C library that would support it without too much trouble.Luana
I had a quick look, didn't find anything suitable, and implemented my own using some of the ideas above. It would be great to find an actual library, so that you can use it on floats and strings with decimal places.Anderer
FWIW AtmelStudio's embedded system printf() appears tragically not to support the ' modifier. From the header: Copyright ... 2007 Joerg Wunsch ... 1993 Regents of the University of California i.e. a BSD derivative.Gorlin
It works on Linux (ArchLinux). That's the solution I was looking for. Thanks!Shaquana
setlocale(LC_NUMERIC, ""); did the trick. Without this line the %'d specifier didn't work as expected.Epifaniaepifano
setlocale(LC_NUMERIC, "en_US"); worked for me on iOSStaff
While this is handy - you don't necessarily want to change the state for this functionality (setlocale).Jaehne
%'d made GNU file give Warning: Printf format is too long for type ... =/Timmons
This doesn't seem to work of AWS EC2 Ubuntu 20 (someone else confirm)Monostylous
H
52

You can do it recursively as follows (beware INT_MIN if you're using two's complement, you'll need extra code to manage that):

void printfcomma2 (int n) {
    if (n < 1000) {
        printf ("%d", n);
        return;
    }
    printfcomma2 (n/1000);
    printf (",%03d", n%1000);
}

void printfcomma (int n) {
    if (n < 0) {
        printf ("-");
        n = -n;
    }
    printfcomma2 (n);
}

A summmary:

  • User calls printfcomma with an integer, the special case of negative numbers is handled by simply printing "-" and making the number positive (this is the bit that won't work with INT_MIN).
  • When you enter printfcomma2, a number less than 1,000 will just print and return.
  • Otherwise the recursion will be called on the next level up (so 1,234,567 will be called with 1,234, then 1) until a number less than 1,000 is found.
  • Then that number will be printed and we'll walk back up the recursion tree, printing a comma and the next number as we go.

There is also the more succinct version though it does unnecessary processing in checking for negative numbers at every level (not that this will matter given the limited number of recursion levels). This one is a complete program for testing:

#include <stdio.h>

void printfcomma (int n) {
    if (n < 0) {
        printf ("-");
        printfcomma (-n);
        return;
    }
    if (n < 1000) {
        printf ("%d", n);
        return;
    }
    printfcomma (n/1000);
    printf (",%03d", n%1000);
}

int main (void) {
    int x[] = {-1234567890, -123456, -12345, -1000, -999, -1,
               0, 1, 999, 1000, 12345, 123456, 1234567890};
    int *px = x;
    while (px != &(x[sizeof(x)/sizeof(*x)])) {
        printf ("%-15d: ", *px);
        printfcomma (*px);
        printf ("\n");
        px++;
    }
    return 0;
}

and the output is:

-1234567890    : -1,234,567,890
-123456        : -123,456
-12345         : -12,345
-1000          : -1,000
-999           : -999
-1             : -1
0              : 0
1              : 1
999            : 999
1000           : 1,000
12345          : 12,345
123456         : 123,456
1234567890     : 1,234,567,890

An iterative solution for those who don't trust recursion (although the only problem with recursion tends to be stack space which will not be an issue here since it'll only be a few levels deep even for a 64-bit integer):

void printfcomma (int n) {
    int n2 = 0;
    int scale = 1;
    if (n < 0) {
        printf ("-");
        n = -n;
    }
    while (n >= 1000) {
        n2 = n2 + scale * (n % 1000);
        n /= 1000;
        scale *= 1000;
    }
    printf ("%d", n);
    while (scale != 1) {
        scale /= 1000;
        n = n2 / scale;
        n2 = n2  % scale;
        printf (",%03d", n);
    }
}

Both of these generate 2,147,483,647 for INT_MAX.


All the code above is for comma-separating three-digit groups but you can use other characters as well, such as a space:

void printfspace2 (int n) {
    if (n < 1000) {
        printf ("%d", n);
        return;
    }
    printfspace2 (n/1000);
    printf (" %03d", n%1000);
}

void printfspace (int n) {
    if (n < 0) {
        printf ("-");
        n = -n;
    }
    printfspace2 (n);
}
Hyposthenia answered 20/9, 2009 at 0:0 Comment(1)
As for "beware INT_MIN": you don't need any extra code, you just need to declare printfcomma2 as void printfcomma (unsigned int n). (Yeah sorry, I know this answer is 14 years old, but how could I let that one go??)Nazar
U
13

Here's a very simple implementation. This function contains no error checking, buffer sizes must be verified by the caller. It also does not work for negative numbers. Such improvements are left as an exercise for the reader.

void format_commas(int n, char *out)
{
    int c;
    char buf[20];
    char *p;

    sprintf(buf, "%d", n);
    c = 2 - strlen(buf) % 3;
    for (p = buf; *p != 0; p++) {
       *out++ = *p;
       if (c == 1) {
           *out++ = ',';
       }
       c = (c + 1) % 3;
    }
    *--out = 0;
}
Uncoil answered 19/9, 2009 at 23:56 Comment(3)
I like this one, it uses sprintf instead of printf, which is useful for embedded systems.Anderer
Quite nice, but with needs some minor tweaks to work for negative numbers.Jaehne
(modified version for negative number support https://mcmap.net/q/88886/-how-to-format-a-number-using-comma-as-thousands-separator-in-c)Jaehne
B
12

Egads! I do this all the time, using gcc/g++ and glibc on linux and yes, the ' operator may be non-standard, but I like the simplicity of it.

#include <stdio.h>
#include <locale.h>

int main()
{
    int bignum=12345678;

    setlocale(LC_ALL,"");

    printf("Big number: %'d\n",bignum);

    return 0;
}

Gives output of:

Big number: 12,345,678

Just have to remember the 'setlocale' call in there, otherwise it won't format anything.

Boner answered 17/9, 2011 at 13:57 Comment(4)
Sadly, this doesn't seem to work in Windows/gcc 4.9.2.Kilian
Well Drat! I would have figured that gcc on any platform would give similar results regardless of OS. Good to know I suppose, wonder why though. Hmmmmm.....Boner
Note that if the C library that's in use does not support the ' flag, then you don't get the desired output — and that's independent of the compiler. The compiler ensures the library function for printf() is called with the format string; it is up to the library function to interpret it. On Windows, it's entirely possible that the CRT library does not provide the support you need — and it matters not which compiler you use.Gastrostomy
BTW, this works in fprintf calls, and with floats as well.Bette
G
5

Perhaps a locale-aware version would be interesting.

#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <limits.h>

static int next_group(char const **grouping) {
    if ((*grouping)[1] == CHAR_MAX)
        return 0;
    if ((*grouping)[1] != '\0')
        ++*grouping;
    return **grouping;
}

size_t commafmt(char   *buf,            /* Buffer for formatted string  */
                int     bufsize,        /* Size of buffer               */
                long    N)              /* Number to convert            */
{
    int i;
    int len = 1;
    int posn = 1;
    int sign = 1;
    char *ptr = buf + bufsize - 1;

    struct lconv *fmt_info = localeconv();
    char const *tsep = fmt_info->thousands_sep;
    char const *group = fmt_info->grouping;
    char const *neg = fmt_info->negative_sign;
    size_t sep_len = strlen(tsep);
    size_t group_len = strlen(group);
    size_t neg_len = strlen(neg);
    int places = (int)*group;

    if (bufsize < 2)
    {
ABORT:
        *buf = '\0';
        return 0;
    }

    *ptr-- = '\0';
    --bufsize;
    if (N < 0L)
    {
        sign = -1;
        N = -N;
    }

    for ( ; len <= bufsize; ++len, ++posn)
    {
        *ptr-- = (char)((N % 10L) + '0');
        if (0L == (N /= 10L))
            break;
        if (places && (0 == (posn % places)))
        {
            places = next_group(&group);
            for (int i=sep_len; i>0; i--) {
                *ptr-- = tsep[i-1];
                if (++len >= bufsize)
                    goto ABORT;
            }
        }
        if (len >= bufsize)
            goto ABORT;
    }

    if (sign < 0)
    {
        if (len >= bufsize)
            goto ABORT;
        for (int i=neg_len; i>0; i--) {
            *ptr-- = neg[i-1];
            if (++len >= bufsize)
                goto ABORT;
        }
    }

    memmove(buf, ++ptr, len + 1);
    return (size_t)len;
}

#ifdef TEST
#include <stdio.h>

#define elements(x) (sizeof(x)/sizeof(x[0]))

void show(long i) {
    char buffer[32];

    commafmt(buffer, sizeof(buffer), i);
    printf("%s\n", buffer);
    commafmt(buffer, sizeof(buffer), -i);
    printf("%s\n", buffer);
}


int main() {

    long inputs[] = {1, 12, 123, 1234, 12345, 123456, 1234567, 12345678 };

    for (int i=0; i<elements(inputs); i++) {
        setlocale(LC_ALL, "");
        show(inputs[i]);
    }
    return 0;
}

#endif

This does have a bug (but one I'd consider fairly minor). On two's complement hardware, it won't convert the most-negative number correctly, because it attempts to convert a negative number to its equivalent positive number with N = -N; In two's complement, the maximally negative number doesn't have a corresponding positive number, unless you promote it to a larger type. One way to get around this is by promoting the number the corresponding unsigned type (but it's is somewhat non-trivial).

Groundling answered 17/3, 2011 at 23:10 Comment(7)
I asked a question directed more toward a cross platform implementation of the format '-flag here: https://mcmap.net/q/89217/-cross-platform-support-for-sprintf-39-s-format-39-flag/2642059 I think this answer perfectly addresses that, doing more testing now. If so I guess I should mark that question as a dupe huh?Arpeggio
OK, first thing I've recognized, it doesn't adjust as the locale adjusts. Why maintain, tsep, place_str, and neg_str at all? Why not just directly use fmt_info's members?Arpeggio
OK, number thing number 2, this code can't handle negative numbers... and I don't exactly know how it could, while (*ptr-- = *neg_str++) doesn't make much sense to me. You're inserting the negative string characters in reverse order.Arpeggio
So... I've eliminated the memory leak, and corrected the bug with negative numbers: ideone.com/gTv8Z4 Unfortunately there's still an issue with multiple character separators or multiple character negative symbols being written to the string backwards. I'm going to try to solve that next...Arpeggio
@JonathanMee: I've updated the code (and added at least a few more test cases, including negative numbers).Groundling
Yup this perfectly answers my question. Now then, should I close my question with a link to this answer? This question seems to be interested in a simplistic solution, and mine in a cross platform solution. In my mind there's a distinction. But I'll deffer to you. If you think it's a dupe I'll close it. If you think it's different enough I'd appreciate if you'd copy-paste your answer.Arpeggio
@JonathanMee: I"m not sure whether your question qualifies as a duplicate or not. Most of the answers here clearly don't answer your question. My answer isn't a very good one to the question as it's currently phrased, with the "simpler the better" language (though that was added 4 years after the question was asked). At the same time, they're closely enough related that it would be kind of a shame to have both with cross reference between the two.Groundling
C
2

Without recursion or string handling, a mathematical approach:

#include <stdio.h>
#include <math.h>

void print_number( int n )
{
    int order_of_magnitude = (n == 0) ? 1 : (int)pow( 10, ((int)floor(log10(abs(n))) / 3) * 3 ) ;

    printf( "%d", n / order_of_magnitude ) ;

    for( n = abs( n ) % order_of_magnitude, order_of_magnitude /= 1000;
        order_of_magnitude > 0;
        n %= order_of_magnitude, order_of_magnitude /= 1000 )
    {
        printf( ",%03d", abs(n / order_of_magnitude) ) ;
    }
}

Similar in principle to Pax's recursive solution, but by calculating the order of magnitude in advance, recursion is avoided (at some considerable expense perhaps).

Note also that the actual character used to separate thousands is locale specific.

Edit:See @Chux's comments below for improvements.

Cha answered 20/9, 2009 at 8:13 Comment(4)
Changing abs(n) to fabs(n) prevents 2's compliment error when performing print_number(INT_MIN).Enuresis
@chux: Good point, but in the % expression, the LHS would be cast back to an int and it would still be broken. It is simpler perhaps to just accept the marginally smaller range of acceptable input or add a test and output "-2,147,483,647" directly for INT_MIN (or whatever INT_MIN is on the platform in question - therein lies another can of worms.Cha
I did test it successfully before suggesting. Hmmm. I see my idea was only meant for the log10(abs(n)) and not elsewhere. Interestingly, your solution works with the single change to log10(fabs(n)) and print_number(INT_MIN) because of the printf(..., abs(n / order_of_magnitude)) which means n = abs(INT_MIN) % order_of_magnitude being negative is OK. If we give-up on INT_MIN, the printf(..., abs(n / order_of_magnitude)) can become printf(..., n / order_of_magnitude). But I suppose working with that worm called "abs(INT_MIN)" is usually a bad thing.Enuresis
New thought: suggest 3 changes log10(fabs(n)), n = abs(n% order_of_magnitude) and printf(",%03d", n/order_of_magnitude). BTW: I would not spend this effort unless I thought you solution was good. No UB, even for INT_MIN.Enuresis
E
2

Another solution, by saving the result into an int array, maximum size of 7 because the long long int type can handle numbers in the range 9,223,372,036,854,775,807 to -9,223,372,036,854,775,807. (Note it is not an unsigned value).

Non-recursive printing function

static void printNumber (int numbers[8], int loc, int negative)
{
    if (negative)
    {
        printf("-");
    }
    if (numbers[1]==-1)//one number
    {
        printf("%d ", numbers[0]);
    }
    else
    {
        printf("%d,", numbers[loc]);
        while(loc--)
        {
            if(loc==0)
            {// last number
                printf("%03d ", numbers[loc]);
                break;
            }
            else
            { // number in between
                printf("%03d,", numbers[loc]);
            }
        }
    }
}

main function call

static void getNumWcommas (long long int n, int numbers[8])
{
    int i;
    int negative=0;
    if (n < 0)
    {
        negative = 1;
        n = -n;
    }
    for(i = 0; i < 7; i++)
    {
        if (n < 1000)
        {
            numbers[i] = n;
            numbers[i+1] = -1;
            break;
        }
        numbers[i] = n%1000;
        n/=1000;
    }

    printNumber(numbers, i, negative);// non recursive print
}

testing output

-9223372036854775807: -9,223,372,036,854,775,807
-1234567890         : -1,234,567,890
-123456             : -123,456
-12345              : -12,345
-1000               : -1,000
-999                : -999
-1                  : -1
0                   : 0
1                   : 1
999                 : 999
1000                : 1,000
12345               : 12,345
123456              : 123,456
1234567890          : 1,234,567,890
9223372036854775807 : 9,223,372,036,854,775,807

In main() function:

int numberSeparated[8];
long long int number = 1234567890LL;
getNumWcommas(number, numberSeparated);

If printing is all that's needed then move int numberSeparated[8]; inside the function getNumWcommas and call it this way getNumWcommas(number).

Egyptian answered 10/9, 2013 at 18:1 Comment(0)
J
2

Based on @Greg Hewgill's, but takes negative numbers into account and returns the string size.

size_t str_format_int_grouped(char dst[16], int num)
{
    char src[16];
    char *p_src = src;
    char *p_dst = dst;

    const char separator = ',';
    int num_len, commas;

    num_len = sprintf(src, "%d", num);

    if (*p_src == '-') {
        *p_dst++ = *p_src++;
        num_len--;
    }

    for (commas = 2 - num_len % 3;
         *p_src;
         commas = (commas + 1) % 3)
    {
        *p_dst++ = *p_src++;
        if (commas == 1) {
            *p_dst++ = separator;
        }
    }
    *--p_dst = '\0';

    return (size_t)(p_dst - dst);
}
Jaehne answered 17/7, 2014 at 4:53 Comment(0)
N
2

Needed to do something similar myself but rather than printing directly, needed to go to a buffer. Here's what I came up with. Works backwards.

unsigned int IntegerToCommaString(char *String, unsigned long long Integer)
{
    unsigned int Digits = 0, Offset, Loop;
    unsigned long long Copy = Integer;

    do {
        Digits++;
        Copy /= 10;
    } while (Copy);

    Digits = Offset = ((Digits - 1) / 3) + Digits;
    String[Offset--] = '\0';

    Copy = Integer;
    Loop = 0;
    do {
        String[Offset] = '0' + (Copy % 10);
        if (!Offset--)
            break;
        if (Loop++ % 3 == 2)
            String[Offset--] = ',';
        Copy /= 10;
    } while (1);

    return Digits;
}

Be aware that it's only designed for unsigned integers and you must ensure that the buffer is large enough.

Narrowminded answered 26/1, 2020 at 21:19 Comment(0)
C
1

There's no real simple way to do this in C. I would just modify an int-to-string function to do it:

void format_number(int n, char * out) {
    int i;
    int digit;
    int out_index = 0;

    for (i = n; i != 0; i /= 10) {
        digit = i % 10;

        if ((out_index + 1) % 4 == 0) {
            out[out_index++] = ',';
        }
        out[out_index++] = digit + '0';
    }
    out[out_index] = '\0';

    // then you reverse the out string as it was converted backwards (it's easier that way).
    // I'll let you figure that one out.
    strrev(out);
}
Carrigan answered 19/9, 2009 at 23:59 Comment(0)
C
1

My answer does not format the result exactly like the illustration in the question, but may fulfill the actual need in some cases with a simple one-liner or macro. One can extend it to generate more thousand-groups as necessary.

The result will look for example as follows:

Value: 0'000'012'345

The code:

printf("Value: %llu'%03lu'%03lu'%03lu\n", (value / 1000 / 1000 / 1000), (value / 1000 / 1000) % 1000, (value / 1000) % 1000, value % 1000);
Colvin answered 23/8, 2015 at 1:41 Comment(2)
Is ' a standard notation equivalent to a , (mathematically, at least) in some part(s) of the world?Carnarvon
@Carnarvon It is a thousand separator in some parts of the world.Colvin
M
1
#include <stdio.h>

void punt(long long n){
    char s[28];
    int i = 27;
    if(n<0){n=-n; putchar('-');} 
    do{
        s[i--] = n%10 + '0';
        if(!(i%4) && n>9)s[i--]='.';
        n /= 10;
    }while(n);
    puts(&s[++i]);
}


int main(){
    punt(2134567890);
    punt(987);
    punt(9876);
    punt(-987);
    punt(-9876);
    punt(-654321);
    punt(0);
    punt(1000000000);
    punt(0x7FFFFFFFFFFFFFFF);
    punt(0x8000000000000001); // -max + 1 ...
}

My solution uses a . instead of a , It is left to the reader to change this.

Marlborough answered 2/5, 2017 at 14:2 Comment(0)
L
1

This is old and there are plenty of answers but the question was not "how can I write a routine to add commas" but "how can it be done in C"? The comments pointed to this direction but on my Linux system with GCC, this works for me:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
int main()
{
    unsetenv("LC_ALL");
    setlocale(LC_NUMERIC, "");
    printf("%'lld\n", 3141592653589);
}

When this is run, I get:

$ cc -g comma.c -o comma && ./comma
3,141,592,653,589

If I unset the LC_ALL variable before running the program the unsetenv is not necessary.

Lipps answered 11/12, 2019 at 21:14 Comment(0)
L
0

Another iterative function

int p(int n) {
  if(n < 0) {
    printf("-");
    n = -n;
  }

  int a[sizeof(int) * CHAR_BIT / 3] = { 0 };
  int *pa = a;
  while(n > 0) {
    *++pa = n % 1000;
    n /= 1000;
  }
  printf("%d", *pa);
  while(pa > a + 1) {
    printf(",%03d", *--pa);
  }
}
Lamprey answered 20/9, 2009 at 11:46 Comment(2)
I am intrigued by the expression used to determine the dimension of the array!? Is there at mathematical justification for that?Cha
ld(10) bits for each decimal digit. Round down to 3. we could divide 3 again (to account for the fact that we store up to 3 digit at once). But i wanted to keep it at an upper limit.Lamprey
R
0

Here is the slimiest, size and speed efficient implementation of this kind of decimal digit formating:

const char *formatNumber (
    int value,
    char *endOfbuffer,
    bool plus)
{
    int savedValue;
    int charCount;

    savedValue = value;
    if (unlikely (value < 0))
        value = - value;
    *--endOfbuffer = 0;
    charCount = -1;
    do
    {
        if (unlikely (++charCount == 3))
        {
            charCount = 0;
            *--endOfbuffer = ',';
        }

        *--endOfbuffer = (char) (value % 10 + '0');
    }
    while ((value /= 10) != 0);

    if (unlikely (savedValue < 0))
        *--endOfbuffer = '-';
    else if (unlikely (plus))
        *--endOfbuffer = '+';

    return endOfbuffer;
}

Use as following:

char buffer[16];
fprintf (stderr, "test : %s.", formatNumber (1234567890, buffer + 16, true));

Output:

test : +1,234,567,890.

Some advantages:

  • Function taking end of string buffer because of reverse ordered formatting. Finally, where is no need in revering generated string (strrev).

  • This function produces one string that can be used in any algo after. It not depends nor require multiple printf/sprintf calls, which is terrible slow and always context specific.

  • Minimum number of divide operators (/, %).
Related answered 26/9, 2013 at 8:7 Comment(3)
What is unlikely?Gurgitation
@Dan: unlikely is likely a hint to the optimizer that the condition is unlikely to be true. See likely()/unlikely() macros in the Linux kernel for more information.Gastrostomy
@JonathanLeffler Oh, huh. Thanks for the link.Gurgitation
R
0

Secure format_commas, with negative numbers:

Because VS < 2015 doesn't implement snprintf, you need to do this

#if defined(_WIN32)
    #define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__)
#endif

And then

char* format_commas(int n, char *out)
{
    int c;
    char buf[100];
    char *p;
    char* q = out; // Backup pointer for return...

    if (n < 0)
    {
        *out++ = '-';
        n = abs(n);
    }


    snprintf(buf, 100, "%d", n);
    c = 2 - strlen(buf) % 3;

    for (p = buf; *p != 0; p++) {
        *out++ = *p;
        if (c == 1) {
            *out++ = '\'';
        }
        c = (c + 1) % 3;
    }
    *--out = 0;

    return q;
}

Example usage:

size_t currentSize = getCurrentRSS();
size_t peakSize = getPeakRSS();


printf("Current size: %d\n", currentSize);
printf("Peak size: %d\n\n\n", peakSize);

char* szcurrentSize = (char*)malloc(100 * sizeof(char));
char* szpeakSize = (char*)malloc(100 * sizeof(char));

printf("Current size (f): %s\n", format_commas((int)currentSize, szcurrentSize));
printf("Peak size (f): %s\n", format_commas((int)currentSize, szpeakSize));

free(szcurrentSize);
free(szpeakSize);
Raggedy answered 28/10, 2015 at 11:38 Comment(0)
B
0

A modified version of @paxdiablo solution, but using WCHAR and wsprinf:

static WCHAR buffer[10];
static int pos = 0;

void printfcomma(const int &n) {
    if (n < 0) {
        wsprintf(buffer + pos, TEXT("-"));
        pos = lstrlen(buffer);
        printfcomma(-n);
        return;
    }
    if (n < 1000) {
        wsprintf(buffer + pos, TEXT("%d"), n);
        pos = lstrlen(buffer);
        return;
    }
    printfcomma(n / 1000);
    wsprintf(buffer + pos, TEXT(",%03d"), n % 1000);
    pos = lstrlen(buffer);
}

void my_sprintf(const int &n)
{
    pos = 0;
    printfcomma(n);
}
Bald answered 21/2, 2016 at 8:43 Comment(0)
M
0

I'm new in C programming. Here is my simple code.

int main()
{
    //  1223 => 1,223
    int n;
    int a[10];
    printf(" n: ");
    scanf_s("%d", &n);
    int i = 0;
    while (n > 0)
    {
        int temp = n % 1000;
        a[i] = temp;
        n /= 1000;
        i++;
    }
    for (int j = i - 1; j >= 0; j--)
    {
        if (j == 0) 
        {
            printf("%d.", a[j]);
        }
        else printf("%d,",a[j]);
    }
    getch();
    return 0;
}
Montanez answered 2/4, 2017 at 9:44 Comment(0)
S
0

Require: <stdio.h> + <string.h>.
Advantage: short, readable, based on the format of scanf-family. And assume no comma on the right of decimal point.

void add_commas(char *in, char *out) {
    int len_in = strlen(in);
    int len_int = -1;                              /* len_int(123.4) = 3 */
        for (int i = 0; i < len_in; ++i) if (in[i] == '.') len_int = i;
    int pos = 0;
    for (int i = 0; i < len_in; ++i) {
        if (i>0 && i<len_int && (len_int-i)%3==0)
            out[pos++] = ',';
        out[pos++] = in[i];
    }
    out[pos] = 0;                                  /* Append the '\0' */
}

Example, to print a formatted double:

#include <stdio.h>
#include <string.h>
#define COUNT_DIGIT_MAX 100
int main() {
    double sum = 30678.7414;
    char input[COUNT_DIGIT_MAX+1] = { 0 }, output[COUNT_DIGIT_MAX+1] = { 0 };
    snprintf(input, COUNT_DIGIT_MAX, "%.2f", sum/12);
    add_commas(input, output);
    printf("%s\n", output);
}

Output:

2,556.56
Shelly answered 3/3, 2021 at 15:27 Comment(6)
add_commas requires the string to contain a decimal point. You should accept numbers without a decimal point too. Also avoid calling to strlen().Nonmetallic
@chqrlie: Thanks for the careful check :) Could you elaborate more about why one should avoid using strlen? (Security reason?)Shelly
No security reason, just avoiding a function call: use in[i] != '\0' instead of i < len_inNonmetallic
@chqrlie: Might be a good one, but I prefer i < len_in to avoid the case \0 is missing. (I assume that strlen would do some security check.)Shelly
@Raining: you assume wrong. strlen() does not do anything fancy, it loops like this: size_t strlen(const char *s) { size_t len; for (len = 0; s[len]; len++); return len; } no security check, no sanity check. If the null terminator is missing, strlen() will invoke undefined behavior just like my suggested code would.Nonmetallic
@chqrlie: Appreciate your explanation! Sorry, I'm not very good at C.Shelly
I
0

Using C++'s std::string as return value with possibly the least overhead and not using any std library functions (sprintf, to_string, etc.).

string group_digs_c(int num)
{
    const unsigned int BUF_SIZE = 128;
    char buf[BUF_SIZE] = { 0 }, * pbuf = &buf[BUF_SIZE - 1];
    int k = 0, neg = 0;
    if (num < 0) { neg = 1; num = num * -1; };

    while(num)
    {
        if (k > 0 && k % 3 == 0)
            *pbuf-- = ',';
        *pbuf-- = (num % 10) + '0';
        num /= 10;
        ++k;
    }

    if (neg)
        *pbuf = '-';
    else
        ++pbuf;

    int cc = buf + BUF_SIZE - pbuf;
    memmove(buf, pbuf, cc);
    buf[cc] = 0;
    string rv = buf;
    return rv;
}
Inland answered 30/1, 2022 at 20:58 Comment(0)
N
0

Here is a simple portable solution relying on sprintf:

#include <stdio.h>

// assuming out points to an array of sufficient size
char *format_commas(char *out, int n, int min_digits) {
    int len = sprintf(out, "%.*d", min_digits, n);
    int i = (*out == '-'), j = len, k = (j - i - 1) / 3;
    out[j + k] = '\0';
    while (k-- > 0) {
        j -= 3;
        out[j + k + 3] = out[j + 2];
        out[j + k + 2] = out[j + 1];
        out[j + k + 1] = out[j + 0];
        out[j + k + 0] = ',';
    }
    return out;
}

The code is easy to adapt for other integer types.

Nonmetallic answered 30/1, 2022 at 22:21 Comment(0)
A
0

There are many interesting contributions here. Some covered all cases, some did not. I picked four of the contributions to test, found some failure cases during testing and then added a solution of my own.

I tested all methods for both accuracy and speed. Even though the OP only requested a solution for one positive number, I upgraded the contributions that didn't cover all possible numbers (so the code below may be slightly different from the original postings). The cases that weren't covered include: 0, negative numbers and the minimum number (INT_MIN).

I changed the declared type from "int" to "long long" since it's more general and all ints will get promoted to long long. I also standardized the call interface to include the number as well as a buffer to contain the formatted string (like some of the contributions) and returned a pointer to the buffer:

char* funcName(long long number_to_format, char* string_buffer);

Including a buffer parameter is considered by some to be "better" than having the function: 1) contain a static buffer (would not be re-entrant) or 2) allocate space for the buffer (would require caller to de-allocate the memory) or 3) print the result directly to stdout (would not be as generally useful since the output may be targeted for a GUI widget, file, pty, pipe, etc.).

I tried to use the same function names as the original contributions to make it easier to refer back to the originals. Contributed functions were modified as needed to pass the accuracy test so that the speed test would be meaningful. The results are included here in case you would like to test more of the contributed techniques for comparison. All code and test code used to generate the results are shown below.

So, here are the results:

Accuracy Test (test cases: LLONG_MIN, -999, -99, 0, 99, 999, LLONG_MAX):
----------------------------------------------------

print_number:
 -9,223,372,036,854,775,808, -999, -99, 0, 99, 999, 9,223,372,036,854,775,807

fmtLocale:
 -9,223,372,036,854,775,808, -999, -99, 0, 99, 999, 9,223,372,036,854,775,807

fmtCommas:
 -9,223,372,036,854,775,808, -999, -99, 0, 99, 999, 9,223,372,036,854,775,807

format_number:
 -9,223,372,036,854,775,808, -999, -99, 0, 99, 999, 9,223,372,036,854,775,807

itoa_commas:
 -9,223,372,036,854,775,808, -999, -99, 0, 99, 999, 9,223,372,036,854,775,807

Speed Test: (1 million calls, values reflect average time per call)
----------------------------------------------------
 print_number:   0.747 us (microsec) per call
    fmtLocale:   0.222 us (microsec) per call
    fmtCommas:   0.212 us (microsec) per call
format_number:   0.124 us (microsec) per call
  itoa_commas:   0.085 us (microsec) per call

Since all contributed techniques are fast (< 1 microsecond on my laptop), unless you need to format millions of numbers, any of the techniques should be acceptable. It's probably best to choose the technique that is most readable to you.

Here is the code:

#line 2 "comma.c"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <time.h>
#include <math.h>
#include <locale.h>
#include <limits.h>

// ----------------------------------------------------------
char* print_number( long long  n, char buf[32] ) {
    long long order_of_magnitude = (n == 0) ? 1
        : (long long)pow( 10, ((long long)floor(log10(fabs(n))) / 3) * 3 ) ;

    char *ptr = buf;
    sprintf(ptr, "%d", n / order_of_magnitude ) ;
    for( n %= order_of_magnitude, order_of_magnitude /= 1000;
            order_of_magnitude > 0;
            n %= order_of_magnitude, order_of_magnitude /= 1000 )
    {
        ptr += strlen(ptr);
        sprintf(ptr, ",%03d", abs(n / order_of_magnitude) );
    }
    return buf;
}

// ----------------------------------------------------------
char* fmtLocale(long long i, char buf[32]) {
    sprintf(buf, "%'lld", i);      // requires setLocale in main
    return buf;
}

// ----------------------------------------------------------
char* fmtCommas(long long num, char dst[32]) {
    char src[27];
    char *p_src = src;
    char *p_dst = dst;

    const char separator = ',';
    int num_len, commas;

    num_len = sprintf(src, "%lld", num);

    if (*p_src == '-') {
        *p_dst++ = *p_src++;
        num_len--;
    }

    for (commas = 2 - num_len % 3;
            *p_src;
            commas = (commas + 1) % 3)
    {
        *p_dst++ = *p_src++;
        if (commas == 1) {
            *p_dst++ = separator;
        }
    }
    *--p_dst = '\0';

    return dst;
}

// ----------------------------------------------------------
char* format_number(long long n, char out[32]) {
    int digit;
    int out_index = 0;
    long long i = (n < 0) ? -n : n;
    if (i == LLONG_MIN) i = LLONG_MAX;      // handle MIN, offset by 1

    if (i == 0) { out[out_index++] = '0'; } // handle 0

    for ( ; i != 0; i /= 10) {
        digit = i % 10;

        if ((out_index + 1) % 4 == 0) {
            out[out_index++] = ',';
        }
        out[out_index++] = digit + '0';
    }

    if (n == LLONG_MIN) { out[0]++; }       // correct for offset
    if (n < 0) { out[out_index++] = '-'; }
    out[out_index] = '\0';

    // then you reverse the out string 
    for (int i=0, j = strlen(out) - 1; i<=j; ++i, --j) {
        char tmp = out[i];
        out[i] = out[j];
        out[j] = tmp;
    }
    return out;
}

// ----------------------------------------------------------
char* itoa_commas(long long i, char buf[32]) {
    char* p = buf + 31;
    *p = '\0';                               // terminate string
    if (i == 0) { *(--p) = '0'; return p; }  // handle 0
    long long n = (i < 0) ? -i : i;
    if (n == LLONG_MIN) n = LLONG_MAX;       // handle MIN, offset by 1

    for (int j=0; 1; ++j) {
        *--p = '0' + n % 10;                 // insert digit
        if ((n /= 10) <= 0) break;
        if (j % 3 == 2) *--p = ',';          // insert a comma
    }

    if (i == LLONG_MIN) { p[24]++; }         // correct for offset
    if (i < 0) { *--p = '-'; }
    return p;
}

// ----------------------------------------------------------
// Test Accuracy
// ----------------------------------------------------------
void test_accuracy(char* name, char* (*func)(long long n, char* buf)) {
    char sbuf[32]; // string buffer

    long long nbuf[] = { LLONG_MIN, -999, -99, 0, 99, 999, LLONG_MAX };
    printf("%s:\n", name);
    printf(" %s", func(nbuf[0], sbuf));
    for (int i=1; i < sizeof(nbuf) / sizeof(long long int); ++i) {
        printf(", %s", func(nbuf[i], sbuf));
    }
    printf("\n");
}

// ----------------------------------------------------------
// Test Speed
// ----------------------------------------------------------
void test_speed(char* name, char* (*func)(long long n, char* buf)) {
    int cycleCount = 1000000;
    //int cycleCount = 1;
    clock_t start;
    double elapsed;
    char sbuf[32]; // string buffer

    start = clock();
    for (int i=0; i < cycleCount; ++i) {
        char* s = func(LLONG_MAX, sbuf);
    }
    elapsed = (double)(clock() - start) / (CLOCKS_PER_SEC / 1000000.0);

    printf("%14s: %7.3f us (microsec) per call\n", name, elapsed / cycleCount);
}

// ----------------------------------------------------------
int main(int argc, char* argv[]){
    setlocale(LC_ALL, "");

    printf("\nAccuracy Test: (LLONG_MIN, -999, 0, 99, LLONG_MAX)\n");
    printf("----------------------------------------------------\n");
    test_accuracy("print_number", print_number);
    test_accuracy("fmtLocale", fmtLocale);
    test_accuracy("fmtCommas", fmtCommas);
    test_accuracy("format_number", format_number);
    test_accuracy("itoa_commas", itoa_commas);

    printf("\nSpeed Test: 1 million calls\n\n");
    printf("----------------------------------------------------\n");
    test_speed("print_number", print_number);
    test_speed("fmtLocale", fmtLocale);
    test_speed("fmtCommas", fmtCommas);
    test_speed("format_number", format_number);
    test_speed("itoa_commas", itoa_commas);

    return 0;
}
Airsickness answered 18/3, 2022 at 1:45 Comment(0)
R
0

It seems sad to have to do this:

setlocale(LC_NUMERIC, getenv("LC_NUMERIC"));
Reckless answered 4/10, 2023 at 15:54 Comment(0)
H
0

Use LC_NUMERIC or LC_ALL according to your locale. For US, you would set LC_ALL to "en_US" whereas for India you would set it to "en_IN"

Hairston answered 22/4, 2024 at 15:43 Comment(0)
B
-1

Can be done pretty easily...

//Make sure output buffer is big enough and that input is a valid null terminated string
void pretty_number(const char* input, char * output)
{
    int iInputLen = strlen(input);
    int iOutputBufferPos = 0;
    for(int i = 0; i < iInputLen; i++)
    {
        if((iInputLen-i) % 3 == 0 && i != 0)
        {
            output[iOutputBufferPos++] = ',';
        }

        output[iOutputBufferPos++] = input[i];
    }

    output[iOutputBufferPos] = '\0';
}

Example call:

char szBuffer[512];
pretty_number("1234567", szBuffer);
//strcmp(szBuffer, "1,234,567") == 0
Blastosphere answered 19/9, 2009 at 23:56 Comment(0)
U
-1
        // separate thousands
        int digit;
        int idx = 0;
        static char buffer[32];
        char* p = &buffer[32];

        *--p = '\0';
        for (int i = fCounter; i != 0; i /= 10)
        {
            digit = i % 10;

            if ((p - buffer) % 4 == 0)
                *--p = ' ';

            *--p = digit + '0';
        }
Unborn answered 28/2, 2014 at 17:27 Comment(1)
This code has a variety of problems. The unused variable idx could go. The code doesn't produce anything for 0. It doesn't handle negative numbers. There's no obvious reason to make buffer a static variable (it limits the reentrancy of the code). There's no explanation of what it does, or mention that after the code is complete, the string pointed at by p contains the formatted string. The least serious problem is that it uses blank instead of comma as the thousands separator. The fact that it doesn't handle zero is the killer problem, though.Gastrostomy
T
-1
void printfcomma ( long long unsigned int n) 
{

    char nstring[100];
     int m;
      int ptr;
       int i,j;


    sprintf(nstring,"%llu",n);
      m=strlen(nstring);

     ptr=m%3;
       if (ptr)
        {   for (i=0;i<ptr;i++)       // print first digits before comma
              printf("%c", nstring[i]); 
           printf(",");
         }
     j=0; 
     for (i=ptr;i<m;i++)      // print the rest inserting commas
          {
            printf("%c",nstring[i]);
            j++;
            if (j%3==0)
              if(i<(m-1)) printf(",");
           }

}
Tmesis answered 12/7, 2014 at 15:1 Comment(3)
At least use proper indentation when posting code. Also perhaps add some explanation on what this does that existing answers don't do yet.Tenedos
This has the virtue of simplicity and is easily understood at a glance.Tmesis
Bogus solution, prints an extra , for numbers below 100, uses printf() where putchar() would fly, uses misleading names, chaotic indentation and far too much code.Nonmetallic

© 2022 - 2025 — McMap. All rights reserved.