home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.sources.misc
- From: allbery@uunet.UU.NET
- Subject: v10i085: numeric formatting function
- Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
-
- Posting-number: Volume 10, Issue 85
- Submitted-by: allbery@uunet.UU.NET
- Archive-name: uformat
-
- Report generators for database managers often use numeric formatting which
- is based on "pictures" like "($,$$$,$$#.&&)" or "<<<<<#". Here's my version
- of a function to perform such formatting. To test it, compile with -DTEST
- to produce the executable "format"; run it with two arguments, a format
- string (quoted to protect it from the shell) and a floating-point number.
- Called from a program, it's uformat(buffer, formatstring, doublevalue) and
- stores its result in buffer.
-
- ++Brandon
-
- -------------------------------------------------------------------------------
- #! /bin/sh
- # This file was wrapped with "dummyshar". "sh" this file to extract.
- # Contents: format.c
- echo extracting 'format.c'
- if test -f 'format.c' -a -z "$1"; then echo Not overwriting 'format.c'; else
- sed 's/^X//' << \EOF > 'format.c'
- X/*
- X * Process a format, as used by various report generators, to format a value.
- X * Format characters:
- X *
- X * * Digit or asterisk prefix
- X * $ Digit or dollar-sign prefix
- X * - Digit or minus-sign prefix if negative
- X * + Digit or sign prefix
- X * ( Digit or left-parenthesis prefix if negative
- X * # Digit or blank prefix
- X * & Digit or zero prefix
- X * ) Right-parenthesis suffix if negative
- X * . Decimal point
- X * , Comma or space prefix
- X * < Digit or space appended after format (left justification)
- X *
- X * This may not be the fastest possible implementation, but it's plenty fast
- X * enough for my purposes.
- X *
- X * This routine uses only fabs(), fmod(), and floor(); it should be compatible
- X * with any system that has a standard C math library.
- X */
- X
- X#ifdef TEST
- X#include <stdio.h>
- X#endif
- X
- Xextern double fabs();
- Xextern double fmod();
- Xextern double floor();
- X
- Xvoid
- Xuformat(buf, val, fmt)
- X char *fmt, *buf;
- X double val;
- X{
- X double decval;
- X int didlead, didsign, pad, signum, overflow;
- X register char *fmtp, *bufp, *decp;
- X char tbuf[1024];
- X
- X signum = (val < 0.0);
- X val = fabs(val);
- X for (decp = fmt; *decp; decp++)
- X if (*decp == '.')
- X break;
- X /*
- X * Make a first pass to calculate a rounding value.
- X */
- X decval = 0.5;
- X for (fmtp = decp; *fmtp; fmtp++)
- X {
- X switch (*fmtp)
- X {
- X case '*':
- X case '$':
- X case '-':
- X case '+':
- X case '(':
- X case '#':
- X case '&':
- X case '<':
- X decval /= 10.0;
- X break;
- X }
- X }
- X val += decval;
- X fmtp = decp;
- X decval = val - floor(val);
- X val = floor(val);
- X pad = 0;
- X didlead = 0;
- X didsign = 0;
- X bufp = tbuf;
- X#ifdef TEST
- X fprintf(stderr, "fmt = %.*s, decp = %s, val = %s%.14g, decval = %.14g\n",
- X (decp - fmt), fmt, decp, (signum? "-": ""), val, decval);
- X#endif
- X while (fmtp != fmt)
- X {
- X switch (*--fmtp)
- X {
- X case '#':
- X case '<':
- X if (val < 1.0)
- X {
- X if (*fmtp == '<')
- X pad++;
- X else
- X *bufp++ = ' ';
- X break;
- X }
- X /*FALLTHROUGH*/
- X case '&':
- X *bufp++ = (int) fmod(val, 10.0) + '0';
- X val /= 10.0;
- X break;
- X case '*':
- X if (val >= 1.0)
- X {
- X *bufp++ = (int) fmod(val, 10.0) + '0';
- X val /= 10.0;
- X break;
- X }
- X *bufp++ = (didlead? ' ': '*');
- X didlead = 1;
- X break;
- X case '$':
- X if (val >= 1.0)
- X {
- X *bufp++ = (int) fmod(val, 10.0) + '0';
- X val /= 10.0;
- X break;
- X }
- X *bufp++ = (didlead? ' ': '$');
- X didlead = 1;
- X break;
- X case '-':
- X if (val >= 1.0)
- X {
- X *bufp++ = (int) fmod(val, 10.0) + '0';
- X val /= 10.0;
- X break;
- X }
- X *bufp++ = (didsign? ' ': (signum? '-': ' '));
- X didsign = 1;
- X break;
- X case '+':
- X if (val >= 1.0)
- X {
- X *bufp++ = (int) fmod(val, 10.0) + '0';
- X val /= 10.0;
- X break;
- X }
- X *bufp++ = (didsign? ' ': (signum? '-': '+'));
- X didsign = 1;
- X break;
- X case '(':
- X if (val >= 1.0)
- X {
- X *bufp++ = (int) fmod(val, 10.0) + '0';
- X val /= 10.0;
- X break;
- X }
- X *bufp++ = (didsign? ' ': (signum? '(': ' '));
- X didsign = 1;
- X break;
- X case ')':
- X *bufp++ = (signum? ')': ' ');
- X break;
- X case ',':
- X *bufp++ = (val < 1.0? ' ': ',');
- X break;
- X default:
- X *bufp++ = *fmtp;
- X }
- X }
- X overflow = (val >= 1.0);
- X while (bufp-- != tbuf)
- X *buf++ = (overflow? '*': *bufp);
- X /*
- X * Decimals turn out to be easy, since we can parse forward and all the
- X * potential digit chars can be treated as "&". Also, extracting digits
- X * is done via (decval *= 10.0; floor(decval)) instead of slow fmod().
- X */
- X while (*decp)
- X {
- X if (overflow)
- X *buf++ = '*';
- X else
- X {
- X switch (*decp)
- X {
- X case '*':
- X case '$':
- X case '-':
- X case '+':
- X case '(':
- X case '#':
- X case '&':
- X case '<':
- X decval *= 10.0;
- X *buf++ = (int) floor(decval) + '0';
- X decval -= floor(decval);
- X break;
- X case ')':
- X *buf++ = (signum? ')': ' ');
- X break;
- X default:
- X *buf++ = *decp;
- X break;
- X }
- X }
- X decp++;
- X }
- X while (pad--)
- X *buf++ = (overflow? '*': ' ');
- X *buf = '\0';
- X}
- X
- X#ifdef TEST
- X
- Xextern double atof();
- X
- Xmain(argc, argv)
- X char **argv;
- X{
- X char buf[1024];
- X
- X if (argc != 3)
- X {
- X fprintf(stderr, "usage: %s format-string value\n", argv[0]);
- X exit(1);
- X }
- X uformat(buf, atof(argv[2]), argv[1]);
- X puts(buf);
- X exit(0);
- X}
- X
- X#endif
- EOF
- chars=`wc -c < 'format.c'`
- if test $chars != 4232; then echo 'format.c' is $chars characters, should be 4232 characters!; fi
- fi
- exit 0
-