3 * Written by D'Arcy J.M. Cain
5 * http://www.druid.net/darcy/
7 * Functions to allow input and output of money normally but store
8 * and handle it as 64 bit ints
10 * A slightly modified version of this file and a discussion of the
11 * workings can be found in the book "Software Solutions in C" by
12 * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7 except that
13 * this version handles 64 bit numbers and so can hold values up to
14 * $92,233,720,368,547,758.07.
16 * src/backend/utils/adt/cash.c
26 #include "libpq/pqformat.h"
27 #include "utils/builtins.h"
28 #include "utils/cash.h"
29 #include "utils/int8.h"
30 #include "utils/numeric.h"
31 #include "utils/pg_locale.h"
34 /*************************************************************************
36 ************************************************************************/
42 static const char *small[] = {
43 "zero", "one", "two", "three", "four", "five", "six", "seven",
44 "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
45 "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
46 "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
48 const char **big = small + 18;
51 /* deal with the simple cases first */
55 /* is it an even multiple of 100? */
58 sprintf(buf, "%s hundred", small[value / 100]);
65 /* is it an even multiple of 10 other than 10? */
66 if (value % 10 == 0 && tu > 10)
67 sprintf(buf, "%s hundred %s",
68 small[value / 100], big[tu / 10]);
70 sprintf(buf, "%s hundred and %s",
71 small[value / 100], small[tu]);
73 sprintf(buf, "%s hundred %s %s",
74 small[value / 100], big[tu / 10], small[tu % 10]);
78 /* is it an even multiple of 10 other than 10? */
79 if (value % 10 == 0 && tu > 10)
80 sprintf(buf, "%s", big[tu / 10]);
82 sprintf(buf, "%s", small[tu]);
84 sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
91 * Convert a string to a cash data type.
92 * Format is [$]###[,]###[.##]
93 * Examples: 123.45 $123.45 $123,456.78
97 cash_in(PG_FUNCTION_ARGS)
99 char *str = PG_GETARG_CSTRING(0);
104 bool seen_dot = false;
112 struct lconv *lconvert = PGLC_localeconv();
115 * frac_digits will be CHAR_MAX in some locales, notably C. However, just
116 * testing for == CHAR_MAX is risky, because of compilers like gcc that
117 * "helpfully" let you alter the platform-standard definition of whether
118 * char is signed or not. If we are so unfortunate as to get compiled
119 * with a nonstandard -fsigned-char or -funsigned-char switch, then our
120 * idea of CHAR_MAX will not agree with libc's. The safest course is not
121 * to test for CHAR_MAX at all, but to impose a range check for plausible
122 * frac_digits values.
124 fpoint = lconvert->frac_digits;
125 if (fpoint < 0 || fpoint > 10)
126 fpoint = 2; /* best guess in this case, I think */
128 /* we restrict dsymbol to be a single byte, but not the other symbols */
129 if (*lconvert->mon_decimal_point != '\0' &&
130 lconvert->mon_decimal_point[1] == '\0')
131 dsymbol = *lconvert->mon_decimal_point;
134 if (*lconvert->mon_thousands_sep != '\0')
135 ssymbol = lconvert->mon_thousands_sep;
136 else /* ssymbol should not equal dsymbol */
137 ssymbol = (dsymbol != ',') ? "," : ".";
138 csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
139 psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
140 nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
143 printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
144 fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
147 /* we need to add all sorts of checking here. For now just */
148 /* strip all leading whitespace and any leading currency symbol */
149 while (isspace((unsigned char) *s))
151 if (strncmp(s, csymbol, strlen(csymbol)) == 0)
152 s += strlen(csymbol);
153 while (isspace((unsigned char) *s))
157 printf("cashin- string is '%s'\n", s);
160 /* a leading minus or paren signifies a negative number */
161 /* again, better heuristics needed */
162 /* XXX - doesn't properly check for balanced parens - djmc */
163 if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
166 s += strlen(nsymbol);
173 else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
174 s += strlen(psymbol);
177 printf("cashin- string is '%s'\n", s);
180 /* allow whitespace and currency symbol after the sign, too */
181 while (isspace((unsigned char) *s))
183 if (strncmp(s, csymbol, strlen(csymbol)) == 0)
184 s += strlen(csymbol);
185 while (isspace((unsigned char) *s))
189 printf("cashin- string is '%s'\n", s);
194 /* we look for digits as long as we have found less */
195 /* than the required number of decimal places */
196 if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
198 value = (value * 10) + (*s - '0');
203 /* decimal point? then start counting fractions... */
204 else if (*s == dsymbol && !seen_dot)
208 /* ignore if "thousands" separator, else we're done */
209 else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
210 s += strlen(ssymbol) - 1;
215 /* round off if there's another digit */
216 if (isdigit((unsigned char) *s) && *s >= '5')
219 /* adjust for less than required decimal places */
220 for (; dec < fpoint; dec++)
224 * should only be trailing digits followed by whitespace, right paren,
225 * trailing sign, and/or trailing currency symbol
227 while (isdigit((unsigned char) *s))
232 if (isspace((unsigned char) *s) || *s == ')')
234 else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
237 s += strlen(nsymbol);
239 else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
240 s += strlen(psymbol);
241 else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
242 s += strlen(csymbol);
245 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
246 errmsg("invalid input syntax for type money: \"%s\"",
250 result = value * sgn;
253 printf("cashin- result is " INT64_FORMAT "\n", result);
256 PG_RETURN_CASH(result);
261 * Function to convert cash to a dollars and cents representation, using
262 * the lc_monetary locale's formatting.
265 cash_out(PG_FUNCTION_ARGS)
267 Cash value = PG_GETARG_CASH(0);
281 struct lconv *lconvert = PGLC_localeconv();
283 /* see comments about frac_digits in cash_in() */
284 points = lconvert->frac_digits;
285 if (points < 0 || points > 10)
286 points = 2; /* best guess in this case, I think */
289 * As with frac_digits, must apply a range check to mon_grouping to avoid
290 * being fooled by variant CHAR_MAX values.
292 mon_group = *lconvert->mon_grouping;
293 if (mon_group <= 0 || mon_group > 6)
296 /* we restrict dsymbol to be a single byte, but not the other symbols */
297 if (*lconvert->mon_decimal_point != '\0' &&
298 lconvert->mon_decimal_point[1] == '\0')
299 dsymbol = *lconvert->mon_decimal_point;
302 if (*lconvert->mon_thousands_sep != '\0')
303 ssymbol = lconvert->mon_thousands_sep;
304 else /* ssymbol should not equal dsymbol */
305 ssymbol = (dsymbol != ',') ? "," : ".";
306 csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
310 /* make the amount positive for digit-reconstruction loop */
312 /* set up formatting data */
313 signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
314 sign_posn = lconvert->n_sign_posn;
315 cs_precedes = lconvert->n_cs_precedes;
316 sep_by_space = lconvert->n_sep_by_space;
320 signsymbol = lconvert->positive_sign;
321 sign_posn = lconvert->p_sign_posn;
322 cs_precedes = lconvert->p_cs_precedes;
323 sep_by_space = lconvert->p_sep_by_space;
326 /* we build the digits+decimal-point+sep string right-to-left in buf[] */
327 bufptr = buf + sizeof(buf) - 1;
331 * Generate digits till there are no non-zero digits left and we emitted
332 * at least one to the left of the decimal point. digit_pos is the
333 * current digit position, with zero as the digit just left of the decimal
334 * point, increasing to the right.
339 if (points && digit_pos == 0)
341 /* insert decimal point, but not if value cannot be fractional */
342 *(--bufptr) = dsymbol;
344 else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
346 /* insert thousands sep, but only to left of radix point */
347 bufptr -= strlen(ssymbol);
348 memcpy(bufptr, ssymbol, strlen(ssymbol));
351 *(--bufptr) = ((uint64) value % 10) + '0';
352 value = ((uint64) value) / 10;
354 } while (value || digit_pos >= 0);
357 * Now, attach currency symbol and sign symbol in the correct order.
359 * The POSIX spec defines these values controlling this code:
362 * 0 Parentheses enclose the quantity and the currency_symbol.
363 * 1 The sign string precedes the quantity and the currency_symbol.
364 * 2 The sign string succeeds the quantity and the currency_symbol.
365 * 3 The sign string precedes the currency_symbol.
366 * 4 The sign string succeeds the currency_symbol.
368 * p/n_cs_precedes: 0 means currency symbol after value, else before it.
371 * 0 No <space> separates the currency symbol and value.
372 * 1 If the currency symbol and sign string are adjacent, a <space>
373 * separates them from the value; otherwise, a <space> separates
374 * the currency symbol from the value.
375 * 2 If the currency symbol and sign string are adjacent, a <space>
376 * separates them; otherwise, a <space> separates the sign string
384 result = psprintf("(%s%s%s)",
386 (sep_by_space == 1) ? " " : "",
389 result = psprintf("(%s%s%s)",
391 (sep_by_space == 1) ? " " : "",
397 result = psprintf("%s%s%s%s%s",
399 (sep_by_space == 2) ? " " : "",
401 (sep_by_space == 1) ? " " : "",
404 result = psprintf("%s%s%s%s%s",
406 (sep_by_space == 2) ? " " : "",
408 (sep_by_space == 1) ? " " : "",
413 result = psprintf("%s%s%s%s%s",
415 (sep_by_space == 1) ? " " : "",
417 (sep_by_space == 2) ? " " : "",
420 result = psprintf("%s%s%s%s%s",
422 (sep_by_space == 1) ? " " : "",
424 (sep_by_space == 2) ? " " : "",
429 result = psprintf("%s%s%s%s%s",
431 (sep_by_space == 2) ? " " : "",
433 (sep_by_space == 1) ? " " : "",
436 result = psprintf("%s%s%s%s%s",
438 (sep_by_space == 1) ? " " : "",
440 (sep_by_space == 2) ? " " : "",
445 result = psprintf("%s%s%s%s%s",
447 (sep_by_space == 2) ? " " : "",
449 (sep_by_space == 1) ? " " : "",
452 result = psprintf("%s%s%s%s%s",
454 (sep_by_space == 1) ? " " : "",
456 (sep_by_space == 2) ? " " : "",
461 PG_RETURN_CSTRING(result);
465 * cash_recv - converts external binary format to cash
468 cash_recv(PG_FUNCTION_ARGS)
470 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
472 PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
476 * cash_send - converts cash to binary format
479 cash_send(PG_FUNCTION_ARGS)
481 Cash arg1 = PG_GETARG_CASH(0);
484 pq_begintypsend(&buf);
485 pq_sendint64(&buf, arg1);
486 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
490 * Comparison functions
494 cash_eq(PG_FUNCTION_ARGS)
496 Cash c1 = PG_GETARG_CASH(0);
497 Cash c2 = PG_GETARG_CASH(1);
499 PG_RETURN_BOOL(c1 == c2);
503 cash_ne(PG_FUNCTION_ARGS)
505 Cash c1 = PG_GETARG_CASH(0);
506 Cash c2 = PG_GETARG_CASH(1);
508 PG_RETURN_BOOL(c1 != c2);
512 cash_lt(PG_FUNCTION_ARGS)
514 Cash c1 = PG_GETARG_CASH(0);
515 Cash c2 = PG_GETARG_CASH(1);
517 PG_RETURN_BOOL(c1 < c2);
521 cash_le(PG_FUNCTION_ARGS)
523 Cash c1 = PG_GETARG_CASH(0);
524 Cash c2 = PG_GETARG_CASH(1);
526 PG_RETURN_BOOL(c1 <= c2);
530 cash_gt(PG_FUNCTION_ARGS)
532 Cash c1 = PG_GETARG_CASH(0);
533 Cash c2 = PG_GETARG_CASH(1);
535 PG_RETURN_BOOL(c1 > c2);
539 cash_ge(PG_FUNCTION_ARGS)
541 Cash c1 = PG_GETARG_CASH(0);
542 Cash c2 = PG_GETARG_CASH(1);
544 PG_RETURN_BOOL(c1 >= c2);
548 cash_cmp(PG_FUNCTION_ARGS)
550 Cash c1 = PG_GETARG_CASH(0);
551 Cash c2 = PG_GETARG_CASH(1);
563 * Add two cash values.
566 cash_pl(PG_FUNCTION_ARGS)
568 Cash c1 = PG_GETARG_CASH(0);
569 Cash c2 = PG_GETARG_CASH(1);
574 PG_RETURN_CASH(result);
579 * Subtract two cash values.
582 cash_mi(PG_FUNCTION_ARGS)
584 Cash c1 = PG_GETARG_CASH(0);
585 Cash c2 = PG_GETARG_CASH(1);
590 PG_RETURN_CASH(result);
595 * Divide cash by cash, returning float8.
598 cash_div_cash(PG_FUNCTION_ARGS)
600 Cash dividend = PG_GETARG_CASH(0);
601 Cash divisor = PG_GETARG_CASH(1);
606 (errcode(ERRCODE_DIVISION_BY_ZERO),
607 errmsg("division by zero")));
609 quotient = (float8) dividend / (float8) divisor;
610 PG_RETURN_FLOAT8(quotient);
615 * Multiply cash by float8.
618 cash_mul_flt8(PG_FUNCTION_ARGS)
620 Cash c = PG_GETARG_CASH(0);
621 float8 f = PG_GETARG_FLOAT8(1);
625 PG_RETURN_CASH(result);
630 * Multiply float8 by cash.
633 flt8_mul_cash(PG_FUNCTION_ARGS)
635 float8 f = PG_GETARG_FLOAT8(0);
636 Cash c = PG_GETARG_CASH(1);
640 PG_RETURN_CASH(result);
645 * Divide cash by float8.
648 cash_div_flt8(PG_FUNCTION_ARGS)
650 Cash c = PG_GETARG_CASH(0);
651 float8 f = PG_GETARG_FLOAT8(1);
656 (errcode(ERRCODE_DIVISION_BY_ZERO),
657 errmsg("division by zero")));
659 result = rint(c / f);
660 PG_RETURN_CASH(result);
665 * Multiply cash by float4.
668 cash_mul_flt4(PG_FUNCTION_ARGS)
670 Cash c = PG_GETARG_CASH(0);
671 float4 f = PG_GETARG_FLOAT4(1);
675 PG_RETURN_CASH(result);
680 * Multiply float4 by cash.
683 flt4_mul_cash(PG_FUNCTION_ARGS)
685 float4 f = PG_GETARG_FLOAT4(0);
686 Cash c = PG_GETARG_CASH(1);
690 PG_RETURN_CASH(result);
695 * Divide cash by float4.
699 cash_div_flt4(PG_FUNCTION_ARGS)
701 Cash c = PG_GETARG_CASH(0);
702 float4 f = PG_GETARG_FLOAT4(1);
707 (errcode(ERRCODE_DIVISION_BY_ZERO),
708 errmsg("division by zero")));
710 result = rint(c / f);
711 PG_RETURN_CASH(result);
716 * Multiply cash by int8.
719 cash_mul_int8(PG_FUNCTION_ARGS)
721 Cash c = PG_GETARG_CASH(0);
722 int64 i = PG_GETARG_INT64(1);
726 PG_RETURN_CASH(result);
731 * Multiply int8 by cash.
734 int8_mul_cash(PG_FUNCTION_ARGS)
736 int64 i = PG_GETARG_INT64(0);
737 Cash c = PG_GETARG_CASH(1);
741 PG_RETURN_CASH(result);
745 * Divide cash by 8-byte integer.
748 cash_div_int8(PG_FUNCTION_ARGS)
750 Cash c = PG_GETARG_CASH(0);
751 int64 i = PG_GETARG_INT64(1);
756 (errcode(ERRCODE_DIVISION_BY_ZERO),
757 errmsg("division by zero")));
759 result = rint(c / i);
761 PG_RETURN_CASH(result);
766 * Multiply cash by int4.
769 cash_mul_int4(PG_FUNCTION_ARGS)
771 Cash c = PG_GETARG_CASH(0);
772 int32 i = PG_GETARG_INT32(1);
776 PG_RETURN_CASH(result);
781 * Multiply int4 by cash.
784 int4_mul_cash(PG_FUNCTION_ARGS)
786 int32 i = PG_GETARG_INT32(0);
787 Cash c = PG_GETARG_CASH(1);
791 PG_RETURN_CASH(result);
796 * Divide cash by 4-byte integer.
800 cash_div_int4(PG_FUNCTION_ARGS)
802 Cash c = PG_GETARG_CASH(0);
803 int32 i = PG_GETARG_INT32(1);
808 (errcode(ERRCODE_DIVISION_BY_ZERO),
809 errmsg("division by zero")));
811 result = rint(c / i);
813 PG_RETURN_CASH(result);
818 * Multiply cash by int2.
821 cash_mul_int2(PG_FUNCTION_ARGS)
823 Cash c = PG_GETARG_CASH(0);
824 int16 s = PG_GETARG_INT16(1);
828 PG_RETURN_CASH(result);
832 * Multiply int2 by cash.
835 int2_mul_cash(PG_FUNCTION_ARGS)
837 int16 s = PG_GETARG_INT16(0);
838 Cash c = PG_GETARG_CASH(1);
842 PG_RETURN_CASH(result);
846 * Divide cash by int2.
850 cash_div_int2(PG_FUNCTION_ARGS)
852 Cash c = PG_GETARG_CASH(0);
853 int16 s = PG_GETARG_INT16(1);
858 (errcode(ERRCODE_DIVISION_BY_ZERO),
859 errmsg("division by zero")));
861 result = rint(c / s);
862 PG_RETURN_CASH(result);
866 * Return larger of two cash values.
869 cashlarger(PG_FUNCTION_ARGS)
871 Cash c1 = PG_GETARG_CASH(0);
872 Cash c2 = PG_GETARG_CASH(1);
875 result = (c1 > c2) ? c1 : c2;
877 PG_RETURN_CASH(result);
881 * Return smaller of two cash values.
884 cashsmaller(PG_FUNCTION_ARGS)
886 Cash c1 = PG_GETARG_CASH(0);
887 Cash c2 = PG_GETARG_CASH(1);
890 result = (c1 < c2) ? c1 : c2;
892 PG_RETURN_CASH(result);
896 * This converts a int4 as well but to a representation using words
897 * Obviously way North American centric - sorry
900 cash_words(PG_FUNCTION_ARGS)
902 Cash value = PG_GETARG_CASH(0);
914 /* work with positive numbers */
918 strcpy(buf, "minus ");
924 /* Now treat as unsigned, to avoid trouble at INT_MIN */
925 val = (uint64) value;
927 m0 = val % INT64CONST(100); /* cents */
928 m1 = (val / INT64CONST(100)) % 1000; /* hundreds */
929 m2 = (val / INT64CONST(100000)) % 1000; /* thousands */
930 m3 = (val / INT64CONST(100000000)) % 1000; /* millions */
931 m4 = (val / INT64CONST(100000000000)) % 1000; /* billions */
932 m5 = (val / INT64CONST(100000000000000)) % 1000; /* trillions */
933 m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
937 strcat(buf, num_word(m6));
938 strcat(buf, " quadrillion ");
943 strcat(buf, num_word(m5));
944 strcat(buf, " trillion ");
949 strcat(buf, num_word(m4));
950 strcat(buf, " billion ");
955 strcat(buf, num_word(m3));
956 strcat(buf, " million ");
961 strcat(buf, num_word(m2));
962 strcat(buf, " thousand ");
966 strcat(buf, num_word(m1));
971 strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
972 strcat(buf, num_word(m0));
973 strcat(buf, m0 == 1 ? " cent" : " cents");
975 /* capitalize output */
976 buf[0] = pg_toupper((unsigned char) buf[0]);
978 /* return as text datum */
979 PG_RETURN_TEXT_P(cstring_to_text(buf));
984 * Convert cash to numeric.
987 cash_numeric(PG_FUNCTION_ARGS)
989 Cash money = PG_GETARG_CASH(0);
997 struct lconv *lconvert = PGLC_localeconv();
999 /* see comments about frac_digits in cash_in() */
1000 fpoint = lconvert->frac_digits;
1001 if (fpoint < 0 || fpoint > 10)
1004 /* compute required scale factor */
1006 for (i = 0; i < fpoint; i++)
1009 /* form the result as money / scale */
1010 amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money));
1011 numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1012 quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale);
1014 /* forcibly round to exactly the intended number of digits */
1015 result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
1017 Int32GetDatum(fpoint)));
1019 PG_RETURN_NUMERIC(result);
1023 * Convert numeric to cash.
1026 numeric_cash(PG_FUNCTION_ARGS)
1028 Datum amount = PG_GETARG_DATUM(0);
1033 Datum numeric_scale;
1034 struct lconv *lconvert = PGLC_localeconv();
1036 /* see comments about frac_digits in cash_in() */
1037 fpoint = lconvert->frac_digits;
1038 if (fpoint < 0 || fpoint > 10)
1041 /* compute required scale factor */
1043 for (i = 0; i < fpoint; i++)
1046 /* multiply the input amount by scale factor */
1047 numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1048 amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
1050 /* note that numeric_int8 will round to nearest integer for us */
1051 result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
1053 PG_RETURN_CASH(result);
1057 * Convert int4 (int) to cash
1060 int4_cash(PG_FUNCTION_ARGS)
1062 int32 amount = PG_GETARG_INT32(0);
1067 struct lconv *lconvert = PGLC_localeconv();
1069 /* see comments about frac_digits in cash_in() */
1070 fpoint = lconvert->frac_digits;
1071 if (fpoint < 0 || fpoint > 10)
1074 /* compute required scale factor */
1076 for (i = 0; i < fpoint; i++)
1079 /* compute amount * scale, checking for overflow */
1080 result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1081 Int64GetDatum(scale)));
1083 PG_RETURN_CASH(result);
1087 * Convert int8 (bigint) to cash
1090 int8_cash(PG_FUNCTION_ARGS)
1092 int64 amount = PG_GETARG_INT64(0);
1097 struct lconv *lconvert = PGLC_localeconv();
1099 /* see comments about frac_digits in cash_in() */
1100 fpoint = lconvert->frac_digits;
1101 if (fpoint < 0 || fpoint > 10)
1104 /* compute required scale factor */
1106 for (i = 0; i < fpoint; i++)
1109 /* compute amount * scale, checking for overflow */
1110 result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1111 Int64GetDatum(scale)));
1113 PG_RETURN_CASH(result);