home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume10 / uformat < prev    next >
Text File  |  1990-02-26  |  6KB  |  252 lines

  1. Newsgroups: comp.sources.misc
  2. From: allbery@uunet.UU.NET
  3. Subject: v10i085: numeric formatting function
  4. Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  5.  
  6. Posting-number: Volume 10, Issue 85
  7. Submitted-by: allbery@uunet.UU.NET
  8. Archive-name: uformat
  9.  
  10. Report generators for database managers often use numeric formatting which
  11. is based on "pictures" like "($,$$$,$$#.&&)" or "<<<<<#".  Here's my version
  12. of a function to perform such formatting.  To test it, compile with -DTEST
  13. to produce the executable "format"; run it with two arguments, a format
  14. string (quoted to protect it from the shell) and a floating-point number.
  15. Called from a program, it's uformat(buffer, formatstring, doublevalue) and
  16. stores its result in buffer.
  17.  
  18. ++Brandon
  19.  
  20. -------------------------------------------------------------------------------
  21. #! /bin/sh
  22. # This file was wrapped with "dummyshar".  "sh" this file to extract.
  23. # Contents:  format.c
  24. echo extracting 'format.c'
  25. if test -f 'format.c' -a -z "$1"; then echo Not overwriting 'format.c'; else
  26. sed 's/^X//' << \EOF > 'format.c'
  27. X/*
  28. X * Process a format, as used by various report generators, to format a value.
  29. X * Format characters:
  30. X *
  31. X * *    Digit or asterisk prefix
  32. X * $    Digit or dollar-sign prefix
  33. X * -    Digit or minus-sign prefix if negative
  34. X * +    Digit or sign prefix
  35. X * (    Digit or left-parenthesis prefix if negative
  36. X * #    Digit or blank prefix
  37. X * &    Digit or zero prefix
  38. X * )    Right-parenthesis suffix if negative
  39. X * .    Decimal point
  40. X * ,    Comma or space prefix
  41. X * <    Digit or space appended after format (left justification)
  42. X *
  43. X * This may not be the fastest possible implementation, but it's plenty fast
  44. X * enough for my purposes.
  45. X *
  46. X * This routine uses only fabs(), fmod(), and floor(); it should be compatible
  47. X * with any system that has a standard C math library.
  48. X */
  49. X
  50. X#ifdef TEST
  51. X#include <stdio.h>
  52. X#endif
  53. X
  54. Xextern double fabs();
  55. Xextern double fmod();
  56. Xextern double floor();
  57. X
  58. Xvoid
  59. Xuformat(buf, val, fmt)
  60. X    char *fmt, *buf;
  61. X    double val;
  62. X{
  63. X    double decval;
  64. X    int didlead, didsign, pad, signum, overflow;
  65. X    register char *fmtp, *bufp, *decp;
  66. X    char tbuf[1024];
  67. X
  68. X    signum = (val < 0.0);
  69. X    val = fabs(val);
  70. X    for (decp = fmt; *decp; decp++)
  71. X    if (*decp == '.')
  72. X        break;
  73. X    /*
  74. X     * Make a first pass to calculate a rounding value.
  75. X     */
  76. X    decval = 0.5;
  77. X    for (fmtp = decp; *fmtp; fmtp++)
  78. X    {
  79. X    switch (*fmtp)
  80. X    {
  81. X    case '*':
  82. X    case '$':
  83. X    case '-':
  84. X    case '+':
  85. X    case '(':
  86. X    case '#':
  87. X    case '&':
  88. X    case '<':
  89. X        decval /= 10.0;
  90. X        break;
  91. X    }
  92. X    }
  93. X    val += decval;
  94. X    fmtp = decp;
  95. X    decval = val - floor(val);
  96. X    val = floor(val);
  97. X    pad = 0;
  98. X    didlead = 0;
  99. X    didsign = 0;
  100. X    bufp = tbuf;
  101. X#ifdef TEST
  102. X    fprintf(stderr, "fmt = %.*s, decp = %s, val = %s%.14g, decval = %.14g\n",
  103. X        (decp - fmt), fmt, decp, (signum? "-": ""), val, decval);
  104. X#endif
  105. X    while (fmtp != fmt)
  106. X    {
  107. X    switch (*--fmtp)
  108. X    {
  109. X    case '#':
  110. X    case '<':
  111. X        if (val < 1.0)
  112. X        {
  113. X        if (*fmtp == '<')
  114. X            pad++;
  115. X        else
  116. X            *bufp++ = ' ';
  117. X        break;
  118. X        }
  119. X        /*FALLTHROUGH*/
  120. X    case '&':
  121. X        *bufp++ = (int) fmod(val, 10.0) + '0';
  122. X        val /= 10.0;
  123. X        break;
  124. X    case '*':
  125. X        if (val >= 1.0)
  126. X        {
  127. X        *bufp++ = (int) fmod(val, 10.0) + '0';
  128. X        val /= 10.0;
  129. X        break;
  130. X        }
  131. X        *bufp++ = (didlead? ' ': '*');
  132. X        didlead = 1;
  133. X        break;
  134. X    case '$':
  135. X        if (val >= 1.0)
  136. X        {
  137. X        *bufp++ = (int) fmod(val, 10.0) + '0';
  138. X        val /= 10.0;
  139. X        break;
  140. X        }
  141. X        *bufp++ = (didlead? ' ': '$');
  142. X        didlead = 1;
  143. X        break;
  144. X    case '-':
  145. X        if (val >= 1.0)
  146. X        {
  147. X        *bufp++ = (int) fmod(val, 10.0) + '0';
  148. X        val /= 10.0;
  149. X        break;
  150. X        }
  151. X        *bufp++ = (didsign? ' ': (signum? '-': ' '));
  152. X        didsign = 1;
  153. X        break;
  154. X    case '+':
  155. X        if (val >= 1.0)
  156. X        {
  157. X        *bufp++ = (int) fmod(val, 10.0) + '0';
  158. X        val /= 10.0;
  159. X        break;
  160. X        }
  161. X        *bufp++ = (didsign? ' ': (signum? '-': '+'));
  162. X        didsign = 1;
  163. X        break;
  164. X    case '(':
  165. X        if (val >= 1.0)
  166. X        {
  167. X        *bufp++ = (int) fmod(val, 10.0) + '0';
  168. X        val /= 10.0;
  169. X        break;
  170. X        }
  171. X        *bufp++ = (didsign? ' ': (signum? '(': ' '));
  172. X        didsign = 1;
  173. X        break;
  174. X    case ')':
  175. X        *bufp++ = (signum? ')': ' ');
  176. X        break;
  177. X    case ',':
  178. X        *bufp++ = (val < 1.0? ' ': ',');
  179. X        break;
  180. X    default:
  181. X        *bufp++ = *fmtp;
  182. X    }
  183. X    }
  184. X    overflow = (val >= 1.0);
  185. X    while (bufp-- != tbuf)
  186. X    *buf++ = (overflow? '*': *bufp);
  187. X    /*
  188. X     * Decimals turn out to be easy, since we can parse forward and all the
  189. X     * potential digit chars can be treated as "&".  Also, extracting digits
  190. X     * is done via (decval *= 10.0; floor(decval)) instead of slow fmod().
  191. X     */
  192. X    while (*decp)
  193. X    {
  194. X    if (overflow)
  195. X        *buf++ = '*';
  196. X    else
  197. X    {
  198. X        switch (*decp)
  199. X        {
  200. X        case '*':
  201. X        case '$':
  202. X        case '-':
  203. X        case '+':
  204. X        case '(':
  205. X        case '#':
  206. X        case '&':
  207. X        case '<':
  208. X        decval *= 10.0;
  209. X        *buf++ = (int) floor(decval) + '0';
  210. X        decval -= floor(decval);
  211. X        break;
  212. X        case ')':
  213. X        *buf++ = (signum? ')': ' ');
  214. X        break;
  215. X        default:
  216. X        *buf++ = *decp;
  217. X        break;
  218. X        }
  219. X    }
  220. X    decp++;
  221. X    }
  222. X    while (pad--)
  223. X    *buf++ = (overflow? '*': ' ');
  224. X    *buf = '\0';
  225. X}
  226. X
  227. X#ifdef TEST
  228. X
  229. Xextern double atof();
  230. X
  231. Xmain(argc, argv)
  232. X    char **argv;
  233. X{
  234. X    char buf[1024];
  235. X
  236. X    if (argc != 3)
  237. X    {
  238. X    fprintf(stderr, "usage: %s format-string value\n", argv[0]);
  239. X    exit(1);
  240. X    }
  241. X    uformat(buf, atof(argv[2]), argv[1]);
  242. X    puts(buf);
  243. X    exit(0);
  244. X}
  245. X
  246. X#endif
  247. EOF
  248. chars=`wc -c < 'format.c'`
  249. if test $chars !=     4232; then echo 'format.c' is $chars characters, should be     4232 characters!; fi
  250. fi
  251. exit 0
  252.