3 * Written by D'Arcy J.M. Cain
5 * Functions to allow input and output of money normally but store
6 * and handle it as int4s
8 * A slightly modified version of this file and a discussion of the
9 * workings can be found in the book "Software Solutions in C" by
10 * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
12 * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.21 1998/01/07 18:46:34 momjian Exp $
22 #include "miscadmin.h"
23 #include "utils/builtins.h"
24 #include "utils/cash.h"
26 static const char *num_word(Cash value);
28 /* when we go to 64 bit values we will have to modify this */
31 #define TERMINATOR (CASH_BUFSZ - 1)
32 #define LAST_PAREN (TERMINATOR - 1)
33 #define LAST_DIGIT (LAST_PAREN - 1)
36 static struct lconv *lconvert = NULL;
40 * Convert a string to a cash data type.
41 * Format is [$]###[,]###[.##]
42 * Examples: 123.45 $123.45 $123,456.78
44 * This is currently implemented as a 32-bit integer.
45 * XXX HACK It looks as though some of the symbols for
46 * monetary values returned by localeconv() can be multiple
47 * bytes/characters. This code assumes one byte only. - tgl 97/04/14
50 cash_in(const char *str)
68 setlocale(LC_ALL, "");
69 lconvert = localeconv();
72 lconvert = localeconv();
74 /* frac_digits in the C locale seems to return CHAR_MAX */
75 /* best guess is 2 in this case I think */
76 fpoint = ((lconvert->frac_digits != CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
78 dsymbol = *lconvert->mon_decimal_point;
79 ssymbol = *lconvert->mon_thousands_sep;
80 csymbol = *lconvert->currency_symbol;
81 psymbol = *lconvert->positive_sign;
82 nsymbol = *lconvert->negative_sign;
93 printf( "cashin- precision %d; decimal %c; thousands %c; currency %c; positive %c; negative %c\n",
94 fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
97 /* we need to add all sorts of checking here. For now just */
98 /* strip all leading whitespace and any leading dollar sign */
99 while (isspace(*s) || *s == csymbol)
102 /* a leading minus or paren signifies a negative number */
103 /* again, better heuristics needed */
104 if (*s == nsymbol || *s == '(')
110 else if (*s == psymbol)
115 while (isspace(*s) || *s == csymbol)
120 /* we look for digits as int4 as we have less */
121 /* than the required number of decimal places */
122 if (isdigit(*s) && dec < fpoint)
124 value = (value * 10) + *s - '0';
129 /* decimal point? then start counting fractions... */
131 else if (*s == dsymbol && !seen_dot)
135 /* "thousands" separator? then skip... */
137 else if (*s == ssymbol)
144 if (isdigit(*s) && *s >= '5')
147 /* adjust for less than required decimal places */
148 for (; dec < fpoint; dec++)
155 while (isspace(*s) || *s == '0' || *s == ')')
159 elog(ERROR, "Bad money external representation %s", str);
161 if (!PointerIsValid(result = palloc(sizeof(Cash))))
162 elog(ERROR, "Memory allocation failed, can't input cash '%s'", str);
164 *result = (value * sgn);
171 * Function to convert cash to a dollars and cents representation.
172 * XXX HACK This code appears to assume US conventions for
173 * positive-valued amounts. - tgl 97/04/14
176 cash_out(Cash *in_value)
178 Cash value = *in_value;
180 char buf[CASH_BUFSZ];
182 int count = LAST_DIGIT;
184 int comma_position = 0;
194 if (lconvert == NULL)
195 lconvert = localeconv();
197 mon_group = *lconvert->mon_grouping;
198 comma = *lconvert->mon_thousands_sep;
199 csymbol = *lconvert->currency_symbol;
200 dsymbol = *lconvert->mon_decimal_point;
201 nsymbol = lconvert->negative_sign;
202 /* frac_digits in the C locale seems to return CHAR_MAX */
203 /* best guess is 2 in this case I think */
204 points = ((lconvert->frac_digits != CHAR_MAX) ? lconvert->frac_digits : 2); /* int_frac_digits? */
205 convention = lconvert->n_sign_posn;
216 point_pos = LAST_DIGIT - points;
218 /* We're playing a little fast and loose with this. Shoot me. */
219 if (!mon_group || mon_group == CHAR_MAX)
222 /* allow more than three decimal points and separate them */
225 point_pos -= (points - 1) / mon_group;
226 comma_position = point_pos % (mon_group + 1);
229 /* we work with positive amounts and add the minus sign at the end */
236 /* allow for trailing negative strings */
237 MemSet(buf, ' ', CASH_BUFSZ);
238 buf[TERMINATOR] = buf[LAST_PAREN] = '\0';
240 while (value || count > (point_pos - 2))
242 if (points && count == point_pos)
243 buf[count--] = dsymbol;
244 else if (comma && count % (mon_group + 1) == comma_position)
245 buf[count--] = comma;
247 buf[count--] = (value % 10) + '0';
251 buf[count] = csymbol;
253 if (buf[LAST_DIGIT] == ',')
254 buf[LAST_DIGIT] = buf[LAST_PAREN];
256 /* see if we need to signify negative amount */
259 if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count + strlen(nsymbol))))
260 elog(ERROR, "Memory allocation failed, can't output cash", NULL);
262 /* Position code of 0 means use parens */
264 sprintf(result, "(%s)", buf + count);
265 else if (convention == 2)
266 sprintf(result, "%s%s", buf + count, nsymbol);
268 sprintf(result, "%s%s", nsymbol, buf + count);
272 if (!PointerIsValid(result = palloc(CASH_BUFSZ + 2 - count)))
273 elog(ERROR, "Memory allocation failed, can't output cash", NULL);
275 strcpy(result, buf + count);
283 cash_eq(Cash *c1, Cash *c2)
285 if (!PointerIsValid(c1) || !PointerIsValid(c2))
292 cash_ne(Cash *c1, Cash *c2)
294 if (!PointerIsValid(c1) || !PointerIsValid(c2))
301 cash_lt(Cash *c1, Cash *c2)
303 if (!PointerIsValid(c1) || !PointerIsValid(c2))
310 cash_le(Cash *c1, Cash *c2)
312 if (!PointerIsValid(c1) || !PointerIsValid(c2))
319 cash_gt(Cash *c1, Cash *c2)
321 if (!PointerIsValid(c1) || !PointerIsValid(c2))
328 cash_ge(Cash *c1, Cash *c2)
330 if (!PointerIsValid(c1) || !PointerIsValid(c2))
338 * Add two cash values.
341 cash_pl(Cash *c1, Cash *c2)
345 if (!PointerIsValid(c1) || !PointerIsValid(c2))
348 if (!PointerIsValid(result = palloc(sizeof(Cash))))
349 elog(ERROR, "Memory allocation failed, can't add cash", NULL);
351 *result = (*c1 + *c2);
358 * Subtract two cash values.
361 cash_mi(Cash *c1, Cash *c2)
365 if (!PointerIsValid(c1) || !PointerIsValid(c2))
368 if (!PointerIsValid(result = palloc(sizeof(Cash))))
369 elog(ERROR, "Memory allocation failed, can't subtract cash", NULL);
371 *result = (*c1 - *c2);
378 * Multiply cash by float8.
381 cash_mul_flt8(Cash *c, float8 *f)
385 if (!PointerIsValid(f) || !PointerIsValid(c))
388 if (!PointerIsValid(result = palloc(sizeof(Cash))))
389 elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
391 *result = ((*f) * (*c));
394 } /* cash_mul_flt8() */
398 * Multiply float8 by cash.
401 flt8_mul_cash(float8 *f, Cash *c)
403 return (cash_mul_flt8(c, f));
404 } /* flt8_mul_cash() */
408 * Divide cash by float8.
410 * XXX Don't know if rounding or truncating is correct behavior.
411 * Round for now. - tgl 97/04/15
414 cash_div_flt8(Cash *c, float8 *f)
418 if (!PointerIsValid(f) || !PointerIsValid(c))
421 if (!PointerIsValid(result = palloc(sizeof(Cash))))
422 elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
425 elog(ERROR, "cash_div: divide by 0.0 error");
427 *result = rint(*c / *f);
430 } /* cash_div_flt8() */
433 * Multiply cash by float4.
436 cash_mul_flt4(Cash *c, float4 *f)
440 if (!PointerIsValid(f) || !PointerIsValid(c))
443 if (!PointerIsValid(result = palloc(sizeof(Cash))))
444 elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
446 *result = ((*f) * (*c));
449 } /* cash_mul_flt4() */
453 * Multiply float4 by float4.
456 flt4_mul_cash(float4 *f, Cash *c)
458 return (cash_mul_flt4(c, f));
459 } /* flt4_mul_cash() */
463 * Divide cash by float4.
465 * XXX Don't know if rounding or truncating is correct behavior.
466 * Round for now. - tgl 97/04/15
469 cash_div_flt4(Cash *c, float4 *f)
473 if (!PointerIsValid(f) || !PointerIsValid(c))
476 if (!PointerIsValid(result = palloc(sizeof(Cash))))
477 elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
480 elog(ERROR, "cash_div: divide by 0.0 error");
482 *result = rint(*c / *f);
485 } /* cash_div_flt4() */
489 * Multiply cash by int4.
492 cash_mul_int4(Cash *c, int4 i)
496 if (!PointerIsValid(c))
499 if (!PointerIsValid(result = palloc(sizeof(Cash))))
500 elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
502 *result = ((i) * (*c));
505 } /* cash_mul_int4() */
509 * Multiply int4 by cash.
512 int4_mul_cash(int4 i, Cash *c)
514 return (cash_mul_int4(c, i));
515 } /* int4_mul_cash() */
519 * Divide cash by 4-byte integer.
521 * XXX Don't know if rounding or truncating is correct behavior.
522 * Round for now. - tgl 97/04/15
525 cash_div_int4(Cash *c, int4 i)
529 if (!PointerIsValid(c))
532 if (!PointerIsValid(result = palloc(sizeof(Cash))))
533 elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
536 elog(ERROR, "cash_idiv: divide by 0 error");
538 *result = rint(*c / i);
541 } /* cash_div_int4() */
545 * Multiply cash by int2.
548 cash_mul_int2(Cash *c, int2 s)
552 if (!PointerIsValid(c))
555 if (!PointerIsValid(result = palloc(sizeof(Cash))))
556 elog(ERROR, "Memory allocation failed, can't multiply cash", NULL);
558 *result = ((s) * (*c));
561 } /* cash_mul_int2() */
565 * Multiply int2 by cash.
568 int2_mul_cash(int2 s, Cash *c)
570 return (cash_mul_int2(c, s));
571 } /* int2_mul_cash() */
575 * Divide cash by int2.
577 * XXX Don't know if rounding or truncating is correct behavior.
578 * Round for now. - tgl 97/04/15
581 cash_div_int2(Cash *c, int2 s)
585 if (!PointerIsValid(c))
588 if (!PointerIsValid(result = palloc(sizeof(Cash))))
589 elog(ERROR, "Memory allocation failed, can't divide cash", NULL);
592 elog(ERROR, "cash_div: divide by 0 error");
594 *result = rint(*c / s);
597 } /* cash_div_int2() */
601 * Return larger of two cash values.
604 cashlarger(Cash *c1, Cash *c2)
608 if (!PointerIsValid(c1) || !PointerIsValid(c2))
611 if (!PointerIsValid(result = palloc(sizeof(Cash))))
612 elog(ERROR, "Memory allocation failed, can't return larger cash", NULL);
614 *result = ((*c1 > *c2) ? *c1 : *c2);
621 * Return smaller of two cash values.
624 cashsmaller(Cash *c1, Cash *c2)
628 if (!PointerIsValid(c1) || !PointerIsValid(c2))
631 if (!PointerIsValid(result = palloc(sizeof(Cash))))
632 elog(ERROR, "Memory allocation failed, can't return smaller cash", NULL);
634 *result = ((*c1 < *c2) ? *c1 : *c2);
637 } /* cashsmaller() */
641 * This converts a int4 as well but to a representation using words
642 * Obviously way North American centric - sorry
645 cash_words_out(Cash *value)
647 static char buf[128];
654 /* work with positive numbers */
658 strcpy(buf, "minus ");
666 m0 = *value % 100; /* cents */
667 m1 = (*value / 100) % 1000; /* hundreds */
668 m2 = (*value / 100000) % 1000; /* thousands */
669 m3 = *value / 100000000 % 1000; /* millions */
673 strcat(buf, num_word(m3));
674 strcat(buf, " million ");
679 strcat(buf, num_word(m2));
680 strcat(buf, " thousand ");
684 strcat(buf, num_word(m1));
689 strcat(buf, (int) (*value / 100) == 1 ? " dollar and " : " dollars and ");
690 strcat(buf, num_word(m0));
691 strcat(buf, m0 == 1 ? " cent" : " cents");
692 *buf = toupper(*buf);
694 } /* cash_words_out() */
697 /*************************************************************************
699 ************************************************************************/
704 static char buf[128];
705 static const char *small[] = {
706 "zero", "one", "two", "three", "four", "five", "six", "seven",
707 "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
708 "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
709 "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety"
711 const char **big = small + 18;
712 int tu = value % 100;
714 /* deal with the simple cases first */
716 return (small[value]);
718 /* is it an even multiple of 100? */
721 sprintf(buf, "%s hundred", small[value / 100]);
728 /* is it an even multiple of 10 other than 10? */
729 if (value % 10 == 0 && tu > 10)
730 sprintf(buf, "%s hundred %s",
731 small[value / 100], big[tu / 10]);
733 sprintf(buf, "%s hundred and %s",
734 small[value / 100], small[tu]);
736 sprintf(buf, "%s hundred %s %s",
737 small[value / 100], big[tu / 10], small[tu % 10]);
742 /* is it an even multiple of 10 other than 10? */
743 if (value % 10 == 0 && tu > 10)
744 sprintf(buf, "%s", big[tu / 10]);
746 sprintf(buf, "%s", small[tu]);
748 sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);