How to separate numbers in three-digit groups in C

Arthur Neilson art at pilikia.pegasus.com
Tue Jun 25 15:15:35 AEST 1991


In article <1991Jun23.174550.14820 at umbc3.umbc.edu> rouben at math16.math.umbc.edu (Rouben Rostamian) writes:
>
>I need help with printing numbers (floating or integer) in C.  I would like
>to display the numbers in three-digit comma-separated format.  For instance,
>the integer 12345678 should be printed as 12,345,678.  The floating point
>number 1234.56789 may be printed as 1,234.5678 or as 1,234.567,8.

I use the function below quite a bit, it formats doubles in a char buffer
according to a picture clause type specification.  I didn't write it, I
got it off the net a long time ago and forget where it came from.

-----8<----- cut here -----8<----- cut here -----8<----- cut here -----8<-----
/*
 *	f o r m a t
 *
 *	Format double value as char string in buffer based
 *	on the picture format passed in the format string.
 *
 *	Recognised format characters:
 *
 *	*	Digit or asterisk prefix
 *	$	Digit or dollar-sign prefix
 *	-	Digit or minus-sign prefix if negative
 *	+	Digit or sign prefix
 *	(	Digit or left-parenthesis prefix if negative
 *	#	Digit or blank prefix
 *	&	Digit or zero prefix
 *	)	Right-parenthesis suffix if negative
 *	.	Decimal point
 *	,	Comma or space prefix
 *	<	Digit or space appended after format (left justification)
 *
 *	This function uses only fabs(), fmod(), and floor(),
 *	it should be compatible with any system that has a
 *	standard C math library.
 */

#include <math.h>

char   *
format(buf, fmt, val)
char   *buf;
char   *fmt;
double  val;
{
	double  decval;
	int     didlead, didsign, pad, signum, overflow;
	register char *fmtp, *bufp, *decp;
	char    tbuf[1024];
	char    *retp = buf;

	signum = (val < 0.0);
	val = fabs(val);
	for (decp = fmt; *decp; decp++)
		if (*decp == '.')
			break;
	/*
	 * Make a first pass to calculate a rounding value.
	 */
	decval = 0.5;
	for (fmtp = decp; *fmtp; fmtp++) {
		switch (*fmtp) {
		case '*':
		case '$':
		case '-':
		case '+':
		case '(':
		case '#':
		case '&':
		case '<':
			decval /= 10.0;
			break;
		}
	}

	val += decval;
	fmtp = decp;
	decval = val - floor(val);
	val = floor(val);
	pad = 0;
	didlead = 0;
	didsign = 0;
	bufp = tbuf;

	while (fmtp != fmt) {
		switch (*--fmtp) {
		case '#':
		case '<':
			if (val < 1.0) {
				if (*fmtp == '<')
					pad++;
				else
					*bufp++ = ' ';
				break;
			}
			/* FALLTHROUGH */
		case '&':
			*bufp++ = (int) fmod(val, 10.0) + '0';
			val /= 10.0;
			break;
		case '*':
			if (val >= 1.0) {
				*bufp++ = (int) fmod(val, 10.0) + '0';
				val /= 10.0;
				break;
			}
			*bufp++ = (didlead ? ' ' : '*');
			didlead = 1;
			break;
		case '$':
			if (val >= 1.0) {
				*bufp++ = (int) fmod(val, 10.0) + '0';
				val /= 10.0;
				break;
			}
			*bufp++ = (didlead ? ' ' : '$');
			didlead = 1;
			break;
		case '-':
			if (val >= 1.0) {
				*bufp++ = (int) fmod(val, 10.0) + '0';
				val /= 10.0;
				break;
			}
			*bufp++ = (didsign ? ' ' : (signum ? '-' : ' '));
			didsign = 1;
			break;
		case '+':
			if (val >= 1.0) {
				*bufp++ = (int) fmod(val, 10.0) + '0';
				val /= 10.0;
				break;
			}
			*bufp++ = (didsign ? ' ' : (signum ? '-' : '+'));
			didsign = 1;
			break;
		case '(':
			if (val >= 1.0) {
				*bufp++ = (int) fmod(val, 10.0) + '0';
				val /= 10.0;
				break;
			}
			*bufp++ = (didsign ? ' ' : (signum ? '(' : ' '));
			didsign = 1;
			break;
		case ')':
			*bufp++ = (signum ? ')' : ' ');
			break;
		case ',':
			*bufp++ = (val < 1.0 ? ' ' : ',');
			break;
		default:
			*bufp++ = *fmtp;
		}
	}

	overflow = (val >= 1.0);
	while (bufp-- != tbuf)
		*buf++ = (overflow ? '*' : *bufp);
	/*
	 * Decimals turn out to be easy, since we can parse forward and all
	 * the potential digit chars can be treated as "&".  Also, extracting
	 * digits is done via (decval *= 10.0; floor(decval)) instead of slow
	 * fmod().
	 */
	while (*decp) {
		if (overflow)
			*buf++ = '*';
		else {
			switch (*decp) {
			case '*':
			case '$':
			case '-':
			case '+':
			case '(':
			case '#':
			case '&':
			case '<':
				decval *= 10.0;
				*buf++ = (int) floor(decval) + '0';
				decval -= floor(decval);
				break;
			case ')':
				*buf++ = (signum ? ')' : ' ');
				break;
			default:
				*buf++ = *decp;
				break;
			}
		}
		decp++;
	}

	while (pad--)
		*buf++ = (overflow ? '*' : ' ');
	*buf = '\0';

	return (retp);
}
-----8<----- cut here -----8<----- cut here -----8<----- cut here -----8<-----
-- 
Arthur W. Neilson III		| INET: art at pilikia.pegasus.com
Bank of Hawaii Tech Support	| UUCP: uunet!ucsd!nosc!pilikia!art



More information about the Comp.unix.questions mailing list