Reputation: 5415
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.
Upvotes: 115
Views: 142991
Reputation: 67224
This one should be really efficient..
#include <stdint.h>
#include <string>
#include <stdio.h>
using std::string;
string toString( uint64_t val ) {
// ULLONG_MAX = 18446744073709551615 [20 digits]
char buf[ 32 ];
snprintf( buf, 32, "%llu", val );
// Go backwards thru the generated string, adding a comma every 3
int len = strlen( buf );
int numSeps = (len - 1) / 3;
// preallocate to avoid multi-reallocs
int s = len + numSeps;
string res( s, 'x' );
s--;
// go right-to-left thru buf
for( int i = len - 1; i >= 0; --i ) {
// the s counter includes offsets for number of commas added so far
res[ s-- ] = buf[ i ];
int numbersPut = len - i;
//printf( "`%s` puts %d nums, i=%d s=%d\n", res.c_str(), numbersPut, i, s ); //debug
if( i&& numbersPut % 3 == 0 ) {
res[ s-- ] = ',';
}
}
return res;
}
int main() {
for( uint64_t i = 0; i < 50; i++ ) {
puts( toString( 1ull << i ).c_str() ) ;
}
}
Upvotes: 0
Reputation: 1
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
.
Upvotes: 0
Reputation: 34
It seems sad to have to do this:
setlocale(LC_NUMERIC, getenv("LC_NUMERIC"));
Upvotes: 0
Reputation: 429
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;
}
Upvotes: 0
Reputation: 144520
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.
Upvotes: 0
Reputation: 71
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;
}
Upvotes: 0
Reputation: 2891
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
Upvotes: 0
Reputation: 881083
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:
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
).printfcomma2
, a number less than 1,000 will just print and return.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);
}
Upvotes: 55
Reputation: 860
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)
.
Upvotes: 2
Reputation: 21
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.
Upvotes: 2
Reputation: 1313
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.
Upvotes: 1
Reputation: 224824
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).
Upvotes: 114
Reputation: 47968
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);
}
Upvotes: 2
Reputation: 489998
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).
Upvotes: 5
Reputation: 31
#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.
Upvotes: 1
Reputation: 475
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;
}
Upvotes: 0
Reputation:
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);
}
Upvotes: 0
Reputation: 82136
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);
Upvotes: 0
Reputation: 4573
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);
Upvotes: 1
Reputation: 9
// 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';
}
Upvotes: -1
Reputation: 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(",");
}
}
Upvotes: -1
Reputation: 9
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.
Upvotes: 0
Reputation: 93446
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.
Upvotes: 2
Reputation: 2016
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.
Upvotes: 13
Reputation: 506837
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);
}
}
Upvotes: 0
Reputation: 176645
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);
}
Upvotes: 1
Reputation: 347196
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
Upvotes: -1
Reputation: 992697
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;
}
Upvotes: 14