Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  cash.c

  Sprache: C
 

/*
 * cash.c
 * Written by D'Arcy J.M. Cain
 * darcy@druid.net
 * http://www.druid.net/darcy/
 *
 * Functions to allow input and output of money normally but store
 * and handle it as 64 bit ints
 *
 * A slightly modified version of this file and a discussion of the
 * workings can be found in the book "Software Solutions in C" by
 * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7 except that
 * this version handles 64 bit numbers and so can hold values up to
 * $92,233,720,368,547,758.07.
 *
 * src/backend/utils/adt/cash.c
 */


#include "postgres.h"

#include <limits.h>
#include <ctype.h>
#include <math.h>

#include "common/int.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/cash.h"
#include "utils/float.h"
#include "utils/numeric.h"
#include "utils/pg_locale.h"


/*************************************************************************
 * Private routines
 ************************************************************************/


static void
append_num_word(StringInfo buf, Cash value)
{
 static const char *const small[] = {
  "zero""one""two""three""four""five""six""seven",
  "eight""nine""ten""eleven""twelve""thirteen""fourteen",
  "fifteen""sixteen""seventeen""eighteen""nineteen""twenty",
  "thirty""forty""fifty""sixty""seventy""eighty""ninety"
 };
 const char *const *big = small + 18;
 int   tu = value % 100;

 /* deal with the simple cases first */
 if (value <= 20)
 {
  appendStringInfoString(buf, small[value]);
  return;
 }

 /* is it an even multiple of 100? */
 if (!tu)
 {
  appendStringInfo(buf, "%s hundred", small[value / 100]);
  return;
 }

 /* more than 99? */
 if (value > 99)
 {
  /* is it an even multiple of 10 other than 10? */
  if (value % 10 == 0 && tu > 10)
   appendStringInfo(buf, "%s hundred %s",
        small[value / 100], big[tu / 10]);
  else if (tu < 20)
   appendStringInfo(buf, "%s hundred and %s",
        small[value / 100], small[tu]);
  else
   appendStringInfo(buf, "%s hundred %s %s",
        small[value / 100], big[tu / 10], small[tu % 10]);
 }
 else
 {
  /* is it an even multiple of 10 other than 10? */
  if (value % 10 == 0 && tu > 10)
   appendStringInfoString(buf, big[tu / 10]);
  else if (tu < 20)
   appendStringInfoString(buf, small[tu]);
  else
   appendStringInfo(buf, "%s %s", big[tu / 10], small[tu % 10]);
 }
}

static inline Cash
cash_pl_cash(Cash c1, Cash c2)
{
 Cash  res;

 if (unlikely(pg_add_s64_overflow(c1, c2, &res)))
  ereport(ERROR,
    (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     errmsg("money out of range")));

 return res;
}

static inline Cash
cash_mi_cash(Cash c1, Cash c2)
{
 Cash  res;

 if (unlikely(pg_sub_s64_overflow(c1, c2, &res)))
  ereport(ERROR,
    (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     errmsg("money out of range")));

 return res;
}

static inline Cash
cash_mul_float8(Cash c, float8 f)
{
 float8  res = rint(float8_mul((float8) c, f));

 if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
  ereport(ERROR,
    (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     errmsg("money out of range")));

 return (Cash) res;
}

static inline Cash
cash_div_float8(Cash c, float8 f)
{
 float8  res = rint(float8_div((float8) c, f));

 if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
  ereport(ERROR,
    (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     errmsg("money out of range")));

 return (Cash) res;
}

static inline Cash
cash_mul_int64(Cash c, int64 i)
{
 Cash  res;

 if (unlikely(pg_mul_s64_overflow(c, i, &res)))
  ereport(ERROR,
    (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     errmsg("money out of range")));

 return res;
}

static inline Cash
cash_div_int64(Cash c, int64 i)
{
 if (unlikely(i == 0))
  ereport(ERROR,
    (errcode(ERRCODE_DIVISION_BY_ZERO),
     errmsg("division by zero")));

 return c / i;
}

/* cash_in()
 * Convert a string to a cash data type.
 * Format is [$]###[,]###[.##]
 * Examples: 123.45 $123.45 $123,456.78
 *
 */

Datum
cash_in(PG_FUNCTION_ARGS)
{
 char    *str = PG_GETARG_CSTRING(0);
 Node    *escontext = fcinfo->context;
 Cash  result;
 Cash  value = 0;
 Cash  dec = 0;
 Cash  sgn = 1;
 bool  seen_dot = false;
 const char *s = str;
 int   fpoint;
 char  dsymbol;
 const char *ssymbol,
      *psymbol,
      *nsymbol,
      *csymbol;
 struct lconv *lconvert = PGLC_localeconv();

 /*
  * frac_digits will be CHAR_MAX in some locales, notably C.  However, just
  * testing for == CHAR_MAX is risky, because of compilers like gcc that
  * "helpfully" let you alter the platform-standard definition of whether
  * char is signed or not.  If we are so unfortunate as to get compiled
  * with a nonstandard -fsigned-char or -funsigned-char switch, then our
  * idea of CHAR_MAX will not agree with libc's. The safest course is not
  * to test for CHAR_MAX at all, but to impose a range check for plausible
  * frac_digits values.
 */

 fpoint = lconvert->frac_digits;
 if (fpoint < 0 || fpoint > 10)
  fpoint = 2;    /* best guess in this case, I think */

 /* we restrict dsymbol to be a single byte, but not the other symbols */
 if (*lconvert->mon_decimal_point != '\0' &&
  lconvert->mon_decimal_point[1] == '\0')
  dsymbol = *lconvert->mon_decimal_point;
 else
  dsymbol = '.';
 if (*lconvert->mon_thousands_sep != '\0')
  ssymbol = lconvert->mon_thousands_sep;
 else      /* ssymbol should not equal dsymbol */
  ssymbol = (dsymbol != ',') ? "," : ".";
 csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
 psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
 nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";

#ifdef CASHDEBUG
 printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
     fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
#endif

 /* we need to add all sorts of checking here.  For now just */
 /* strip all leading whitespace and any leading currency symbol */
 while (isspace((unsigned char) *s))
  s++;
 if (strncmp(s, csymbol, strlen(csymbol)) == 0)
  s += strlen(csymbol);
 while (isspace((unsigned char) *s))
  s++;

#ifdef CASHDEBUG
 printf("cashin- string is '%s'\n", s);
#endif

 /* a leading minus or paren signifies a negative number */
 /* again, better heuristics needed */
 /* XXX - doesn't properly check for balanced parens - djmc */
 if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
 {
  sgn = -1;
  s += strlen(nsymbol);
 }
 else if (*s == '(')
 {
  sgn = -1;
  s++;
 }
 else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
  s += strlen(psymbol);

#ifdef CASHDEBUG
 printf("cashin- string is '%s'\n", s);
#endif

 /* allow whitespace and currency symbol after the sign, too */
 while (isspace((unsigned char) *s))
  s++;
 if (strncmp(s, csymbol, strlen(csymbol)) == 0)
  s += strlen(csymbol);
 while (isspace((unsigned char) *s))
  s++;

#ifdef CASHDEBUG
 printf("cashin- string is '%s'\n", s);
#endif

 /*
  * We accumulate the absolute amount in "value" and then apply the sign at
  * the end.  (The sign can appear before or after the digits, so it would
  * be more complicated to do otherwise.)  Because of the larger range of
  * negative signed integers, we build "value" in the negative and then
  * flip the sign at the end, catching most-negative-number overflow if
  * necessary.
 */


 for (; *s; s++)
 {
  /*
   * We look for digits as long as we have found less than the required
   * number of decimal places.
 */

  if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
  {
   int8  digit = *s - '0';

   if (pg_mul_s64_overflow(value, 10, &value) ||
    pg_sub_s64_overflow(value, digit, &value))
    ereturn(escontext, (Datum) 0,
      (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
       errmsg("value \"%s\" is out of range for type %s",
        str, "money")));

   if (seen_dot)
    dec++;
  }
  /* decimal point? then start counting fractions... */
  else if (*s == dsymbol && !seen_dot)
  {
   seen_dot = true;
  }
  /* ignore if "thousands" separator, else we're done */
  else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
   s += strlen(ssymbol) - 1;
  else
   break;
 }

 /* round off if there's another digit */
 if (isdigit((unsigned char) *s) && *s >= '5')
 {
  /* remember we build the value in the negative */
  if (pg_sub_s64_overflow(value, 1, &value))
   ereturn(escontext, (Datum) 0,
     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
      errmsg("value \"%s\" is out of range for type %s",
       str, "money")));
 }

 /* adjust for less than required decimal places */
 for (; dec < fpoint; dec++)
 {
  if (pg_mul_s64_overflow(value, 10, &value))
   ereturn(escontext, (Datum) 0,
     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
      errmsg("value \"%s\" is out of range for type %s",
       str, "money")));
 }

 /*
  * should only be trailing digits followed by whitespace, right paren,
  * trailing sign, and/or trailing currency symbol
 */

 while (isdigit((unsigned char) *s))
  s++;

 while (*s)
 {
  if (isspace((unsigned char) *s) || *s == ')')
   s++;
  else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
  {
   sgn = -1;
   s += strlen(nsymbol);
  }
  else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
   s += strlen(psymbol);
  else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
   s += strlen(csymbol);
  else
   ereturn(escontext, (Datum) 0,
     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
      errmsg("invalid input syntax for type %s: \"%s\"",
       "money", str)));
 }

 /*
  * If the value is supposed to be positive, flip the sign, but check for
  * the most negative number.
 */

 if (sgn > 0)
 {
  if (value == PG_INT64_MIN)
   ereturn(escontext, (Datum) 0,
     (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
      errmsg("value \"%s\" is out of range for type %s",
       str, "money")));
  result = -value;
 }
 else
  result = value;

#ifdef CASHDEBUG
 printf("cashin- result is " INT64_FORMAT "\n", result);
#endif

 PG_RETURN_CASH(result);
}


/* cash_out()
 * Function to convert cash to a dollars and cents representation, using
 * the lc_monetary locale's formatting.
 */

Datum
cash_out(PG_FUNCTION_ARGS)
{
 Cash  value = PG_GETARG_CASH(0);
 uint64  uvalue;
 char    *result;
 char  buf[128];
 char    *bufptr;
 int   digit_pos;
 int   points,
    mon_group;
 char  dsymbol;
 const char *ssymbol,
      *csymbol,
      *signsymbol;
 char  sign_posn,
    cs_precedes,
    sep_by_space;
 struct lconv *lconvert = PGLC_localeconv();

 /* see comments about frac_digits in cash_in() */
 points = lconvert->frac_digits;
 if (points < 0 || points > 10)
  points = 2;    /* best guess in this case, I think */

 /*
  * As with frac_digits, must apply a range check to mon_grouping to avoid
  * being fooled by variant CHAR_MAX values.
 */

 mon_group = *lconvert->mon_grouping;
 if (mon_group <= 0 || mon_group > 6)
  mon_group = 3;

 /* we restrict dsymbol to be a single byte, but not the other symbols */
 if (*lconvert->mon_decimal_point != '\0' &&
  lconvert->mon_decimal_point[1] == '\0')
  dsymbol = *lconvert->mon_decimal_point;
 else
  dsymbol = '.';
 if (*lconvert->mon_thousands_sep != '\0')
  ssymbol = lconvert->mon_thousands_sep;
 else      /* ssymbol should not equal dsymbol */
  ssymbol = (dsymbol != ',') ? "," : ".";
 csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";

 if (value < 0)
 {
  /* set up formatting data */
  signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
  sign_posn = lconvert->n_sign_posn;
  cs_precedes = lconvert->n_cs_precedes;
  sep_by_space = lconvert->n_sep_by_space;
 }
 else
 {
  signsymbol = lconvert->positive_sign;
  sign_posn = lconvert->p_sign_posn;
  cs_precedes = lconvert->p_cs_precedes;
  sep_by_space = lconvert->p_sep_by_space;
 }

 /* make the amount positive for digit-reconstruction loop */
 uvalue = pg_abs_s64(value);

 /* we build the digits+decimal-point+sep string right-to-left in buf[] */
 bufptr = buf + sizeof(buf) - 1;
 *bufptr = '\0';

 /*
  * Generate digits till there are no non-zero digits left and we emitted
  * at least one to the left of the decimal point.  digit_pos is the
  * current digit position, with zero as the digit just left of the decimal
  * point, increasing to the right.
 */

 digit_pos = points;
 do
 {
  if (points && digit_pos == 0)
  {
   /* insert decimal point, but not if value cannot be fractional */
   *(--bufptr) = dsymbol;
  }
  else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
  {
   /* insert thousands sep, but only to left of radix point */
   bufptr -= strlen(ssymbol);
   memcpy(bufptr, ssymbol, strlen(ssymbol));
  }

  *(--bufptr) = (uvalue % 10) + '0';
  uvalue = uvalue / 10;
  digit_pos--;
 } while (uvalue || digit_pos >= 0);

 /*----------
  * Now, attach currency symbol and sign symbol in the correct order.
  *
  * The POSIX spec defines these values controlling this code:
  *
  * p/n_sign_posn:
  * 0 Parentheses enclose the quantity and the currency_symbol.
  * 1 The sign string precedes the quantity and the currency_symbol.
  * 2 The sign string succeeds the quantity and the currency_symbol.
  * 3 The sign string precedes the currency_symbol.
  * 4 The sign string succeeds the currency_symbol.
  *
  * p/n_cs_precedes: 0 means currency symbol after value, else before it.
  *
  * p/n_sep_by_space:
  * 0 No <space> separates the currency symbol and value.
  * 1 If the currency symbol and sign string are adjacent, a <space>
  *  separates them from the value; otherwise, a <space> separates
  *  the currency symbol from the value.
  * 2 If the currency symbol and sign string are adjacent, a <space>
  *  separates them; otherwise, a <space> separates the sign string
  *  from the value.
  *----------
 */

 switch (sign_posn)
 {
  case 0:
   if (cs_precedes)
    result = psprintf("(%s%s%s)",
          csymbol,
          (sep_by_space == 1) ? " " : "",
          bufptr);
   else
    result = psprintf("(%s%s%s)",
          bufptr,
          (sep_by_space == 1) ? " " : "",
          csymbol);
   break;
  case 1:
  default:
   if (cs_precedes)
    result = psprintf("%s%s%s%s%s",
          signsymbol,
          (sep_by_space == 2) ? " " : "",
          csymbol,
          (sep_by_space == 1) ? " " : "",
          bufptr);
   else
    result = psprintf("%s%s%s%s%s",
          signsymbol,
          (sep_by_space == 2) ? " " : "",
          bufptr,
          (sep_by_space == 1) ? " " : "",
          csymbol);
   break;
  case 2:
   if (cs_precedes)
    result = psprintf("%s%s%s%s%s",
          csymbol,
          (sep_by_space == 1) ? " " : "",
          bufptr,
          (sep_by_space == 2) ? " " : "",
          signsymbol);
   else
    result = psprintf("%s%s%s%s%s",
          bufptr,
          (sep_by_space == 1) ? " " : "",
          csymbol,
          (sep_by_space == 2) ? " " : "",
          signsymbol);
   break;
  case 3:
   if (cs_precedes)
    result = psprintf("%s%s%s%s%s",
          signsymbol,
          (sep_by_space == 2) ? " " : "",
          csymbol,
          (sep_by_space == 1) ? " " : "",
          bufptr);
   else
    result = psprintf("%s%s%s%s%s",
          bufptr,
          (sep_by_space == 1) ? " " : "",
          signsymbol,
          (sep_by_space == 2) ? " " : "",
          csymbol);
   break;
  case 4:
   if (cs_precedes)
    result = psprintf("%s%s%s%s%s",
          csymbol,
          (sep_by_space == 2) ? " " : "",
          signsymbol,
          (sep_by_space == 1) ? " " : "",
          bufptr);
   else
    result = psprintf("%s%s%s%s%s",
          bufptr,
          (sep_by_space == 1) ? " " : "",
          csymbol,
          (sep_by_space == 2) ? " " : "",
          signsymbol);
   break;
 }

 PG_RETURN_CSTRING(result);
}

/*
 *  cash_recv   - converts external binary format to cash
 */

Datum
cash_recv(PG_FUNCTION_ARGS)
{
 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);

 PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
}

/*
 *  cash_send   - converts cash to binary format
 */

Datum
cash_send(PG_FUNCTION_ARGS)
{
 Cash  arg1 = PG_GETARG_CASH(0);
 StringInfoData buf;

 pq_begintypsend(&buf);
 pq_sendint64(&buf, arg1);
 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

/*
 * Comparison functions
 */


Datum
cash_eq(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 PG_RETURN_BOOL(c1 == c2);
}

Datum
cash_ne(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 PG_RETURN_BOOL(c1 != c2);
}

Datum
cash_lt(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 PG_RETURN_BOOL(c1 < c2);
}

Datum
cash_le(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 PG_RETURN_BOOL(c1 <= c2);
}

Datum
cash_gt(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 PG_RETURN_BOOL(c1 > c2);
}

Datum
cash_ge(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 PG_RETURN_BOOL(c1 >= c2);
}

Datum
cash_cmp(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 if (c1 > c2)
  PG_RETURN_INT32(1);
 else if (c1 == c2)
  PG_RETURN_INT32(0);
 else
  PG_RETURN_INT32(-1);
}


/* cash_pl()
 * Add two cash values.
 */

Datum
cash_pl(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 PG_RETURN_CASH(cash_pl_cash(c1, c2));
}


/* cash_mi()
 * Subtract two cash values.
 */

Datum
cash_mi(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);

 PG_RETURN_CASH(cash_mi_cash(c1, c2));
}


/* cash_div_cash()
 * Divide cash by cash, returning float8.
 */

Datum
cash_div_cash(PG_FUNCTION_ARGS)
{
 Cash  dividend = PG_GETARG_CASH(0);
 Cash  divisor = PG_GETARG_CASH(1);
 float8  quotient;

 if (divisor == 0)
  ereport(ERROR,
    (errcode(ERRCODE_DIVISION_BY_ZERO),
     errmsg("division by zero")));

 quotient = (float8) dividend / (float8) divisor;
 PG_RETURN_FLOAT8(quotient);
}


/* cash_mul_flt8()
 * Multiply cash by float8.
 */

Datum
cash_mul_flt8(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 float8  f = PG_GETARG_FLOAT8(1);

 PG_RETURN_CASH(cash_mul_float8(c, f));
}


/* flt8_mul_cash()
 * Multiply float8 by cash.
 */

Datum
flt8_mul_cash(PG_FUNCTION_ARGS)
{
 float8  f = PG_GETARG_FLOAT8(0);
 Cash  c = PG_GETARG_CASH(1);

 PG_RETURN_CASH(cash_mul_float8(c, f));
}


/* cash_div_flt8()
 * Divide cash by float8.
 */

Datum
cash_div_flt8(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 float8  f = PG_GETARG_FLOAT8(1);

 PG_RETURN_CASH(cash_div_float8(c, f));
}


/* cash_mul_flt4()
 * Multiply cash by float4.
 */

Datum
cash_mul_flt4(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 float4  f = PG_GETARG_FLOAT4(1);

 PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
}


/* flt4_mul_cash()
 * Multiply float4 by cash.
 */

Datum
flt4_mul_cash(PG_FUNCTION_ARGS)
{
 float4  f = PG_GETARG_FLOAT4(0);
 Cash  c = PG_GETARG_CASH(1);

 PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
}


/* cash_div_flt4()
 * Divide cash by float4.
 *
 */

Datum
cash_div_flt4(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 float4  f = PG_GETARG_FLOAT4(1);

 PG_RETURN_CASH(cash_div_float8(c, (float8) f));
}


/* cash_mul_int8()
 * Multiply cash by int8.
 */

Datum
cash_mul_int8(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 int64  i = PG_GETARG_INT64(1);

 PG_RETURN_CASH(cash_mul_int64(c, i));
}


/* int8_mul_cash()
 * Multiply int8 by cash.
 */

Datum
int8_mul_cash(PG_FUNCTION_ARGS)
{
 int64  i = PG_GETARG_INT64(0);
 Cash  c = PG_GETARG_CASH(1);

 PG_RETURN_CASH(cash_mul_int64(c, i));
}

/* cash_div_int8()
 * Divide cash by 8-byte integer.
 */

Datum
cash_div_int8(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 int64  i = PG_GETARG_INT64(1);

 PG_RETURN_CASH(cash_div_int64(c, i));
}


/* cash_mul_int4()
 * Multiply cash by int4.
 */

Datum
cash_mul_int4(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 int32  i = PG_GETARG_INT32(1);

 PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
}


/* int4_mul_cash()
 * Multiply int4 by cash.
 */

Datum
int4_mul_cash(PG_FUNCTION_ARGS)
{
 int32  i = PG_GETARG_INT32(0);
 Cash  c = PG_GETARG_CASH(1);

 PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
}


/* cash_div_int4()
 * Divide cash by 4-byte integer.
 *
 */

Datum
cash_div_int4(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 int32  i = PG_GETARG_INT32(1);

 PG_RETURN_CASH(cash_div_int64(c, (int64) i));
}


/* cash_mul_int2()
 * Multiply cash by int2.
 */

Datum
cash_mul_int2(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 int16  s = PG_GETARG_INT16(1);

 PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
}

/* int2_mul_cash()
 * Multiply int2 by cash.
 */

Datum
int2_mul_cash(PG_FUNCTION_ARGS)
{
 int16  s = PG_GETARG_INT16(0);
 Cash  c = PG_GETARG_CASH(1);

 PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
}

/* cash_div_int2()
 * Divide cash by int2.
 *
 */

Datum
cash_div_int2(PG_FUNCTION_ARGS)
{
 Cash  c = PG_GETARG_CASH(0);
 int16  s = PG_GETARG_INT16(1);

 PG_RETURN_CASH(cash_div_int64(c, (int64) s));
}

/* cashlarger()
 * Return larger of two cash values.
 */

Datum
cashlarger(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);
 Cash  result;

 result = (c1 > c2) ? c1 : c2;

 PG_RETURN_CASH(result);
}

/* cashsmaller()
 * Return smaller of two cash values.
 */

Datum
cashsmaller(PG_FUNCTION_ARGS)
{
 Cash  c1 = PG_GETARG_CASH(0);
 Cash  c2 = PG_GETARG_CASH(1);
 Cash  result;

 result = (c1 < c2) ? c1 : c2;

 PG_RETURN_CASH(result);
}

/* cash_words()
 * This converts an int4 as well but to a representation using words
 * Obviously way North American centric - sorry
 */

Datum
cash_words(PG_FUNCTION_ARGS)
{
 Cash  value = PG_GETARG_CASH(0);
 uint64  val;
 StringInfoData buf;
 text    *res;
 Cash  dollars;
 Cash  m0;
 Cash  m1;
 Cash  m2;
 Cash  m3;
 Cash  m4;
 Cash  m5;
 Cash  m6;

 initStringInfo(&buf);

 /* work with positive numbers */
 if (value < 0)
 {
  value = -value;
  appendStringInfoString(&buf, "minus ");
 }

 /* Now treat as unsigned, to avoid trouble at INT_MIN */
 val = (uint64) value;

 dollars = val / INT64CONST(100);
 m0 = val % INT64CONST(100); /* cents */
 m1 = (val / INT64CONST(100)) % 1000/* hundreds */
 m2 = (val / INT64CONST(100000)) % 1000/* thousands */
 m3 = (val / INT64CONST(100000000)) % 1000/* millions */
 m4 = (val / INT64CONST(100000000000)) % 1000/* billions */
 m5 = (val / INT64CONST(100000000000000)) % 1000/* trillions */
 m6 = (val / INT64CONST(100000000000000000)) % 1000/* quadrillions */

 if (m6)
 {
  append_num_word(&buf, m6);
  appendStringInfoString(&buf, " quadrillion ");
 }

 if (m5)
 {
  append_num_word(&buf, m5);
  appendStringInfoString(&buf, " trillion ");
 }

 if (m4)
 {
  append_num_word(&buf, m4);
  appendStringInfoString(&buf, " billion ");
 }

 if (m3)
 {
  append_num_word(&buf, m3);
  appendStringInfoString(&buf, " million ");
 }

 if (m2)
 {
  append_num_word(&buf, m2);
  appendStringInfoString(&buf, " thousand ");
 }

 if (m1)
  append_num_word(&buf, m1);

 if (dollars == 0)
  appendStringInfoString(&buf, "zero");

 appendStringInfoString(&buf, dollars == 1 ? " dollar and " : " dollars and ");
 append_num_word(&buf, m0);
 appendStringInfoString(&buf, m0 == 1 ? " cent" : " cents");

 /* capitalize output */
 buf.data[0] = pg_toupper((unsigned char) buf.data[0]);

 /* return as text datum */
 res = cstring_to_text_with_len(buf.data, buf.len);
 pfree(buf.data);
 PG_RETURN_TEXT_P(res);
}


/* cash_numeric()
 * Convert cash to numeric.
 */

Datum
cash_numeric(PG_FUNCTION_ARGS)
{
 Cash  money = PG_GETARG_CASH(0);
 Datum  result;
 int   fpoint;
 struct lconv *lconvert = PGLC_localeconv();

 /* see comments about frac_digits in cash_in() */
 fpoint = lconvert->frac_digits;
 if (fpoint < 0 || fpoint > 10)
  fpoint = 2;

 /* convert the integral money value to numeric */
 result = NumericGetDatum(int64_to_numeric(money));

 /* scale appropriately, if needed */
 if (fpoint > 0)
 {
  int64  scale;
  int   i;
  Datum  numeric_scale;
  Datum  quotient;

  /* compute required scale factor */
  scale = 1;
  for (i = 0; i < fpoint; i++)
   scale *= 10;
  numeric_scale = NumericGetDatum(int64_to_numeric(scale));

  /*
   * Given integral inputs approaching INT64_MAX, select_div_scale()
   * might choose a result scale of zero, causing loss of fractional
   * digits in the quotient.  We can ensure an exact result by setting
   * the dscale of either input to be at least as large as the desired
   * result scale.  numeric_round() will do that for us.
 */

  numeric_scale = DirectFunctionCall2(numeric_round,
           numeric_scale,
           Int32GetDatum(fpoint));

  /* Now we can safely divide ... */
  quotient = DirectFunctionCall2(numeric_div, result, numeric_scale);

  /* ... and forcibly round to exactly the intended number of digits */
  result = DirectFunctionCall2(numeric_round,
          quotient,
          Int32GetDatum(fpoint));
 }

 PG_RETURN_DATUM(result);
}

/* numeric_cash()
 * Convert numeric to cash.
 */

Datum
numeric_cash(PG_FUNCTION_ARGS)
{
 Datum  amount = PG_GETARG_DATUM(0);
 Cash  result;
 int   fpoint;
 int64  scale;
 int   i;
 Datum  numeric_scale;
 struct lconv *lconvert = PGLC_localeconv();

 /* see comments about frac_digits in cash_in() */
 fpoint = lconvert->frac_digits;
 if (fpoint < 0 || fpoint > 10)
  fpoint = 2;

 /* compute required scale factor */
 scale = 1;
 for (i = 0; i < fpoint; i++)
  scale *= 10;

 /* multiply the input amount by scale factor */
 numeric_scale = NumericGetDatum(int64_to_numeric(scale));
 amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);

 /* note that numeric_int8 will round to nearest integer for us */
 result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));

 PG_RETURN_CASH(result);
}

/* int4_cash()
 * Convert int4 (int) to cash
 */

Datum
int4_cash(PG_FUNCTION_ARGS)
{
 int32  amount = PG_GETARG_INT32(0);
 Cash  result;
 int   fpoint;
 int64  scale;
 int   i;
 struct lconv *lconvert = PGLC_localeconv();

 /* see comments about frac_digits in cash_in() */
 fpoint = lconvert->frac_digits;
 if (fpoint < 0 || fpoint > 10)
  fpoint = 2;

 /* compute required scale factor */
 scale = 1;
 for (i = 0; i < fpoint; i++)
  scale *= 10;

 /* compute amount * scale, checking for overflow */
 result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
              Int64GetDatum(scale)));

 PG_RETURN_CASH(result);
}

/* int8_cash()
 * Convert int8 (bigint) to cash
 */

Datum
int8_cash(PG_FUNCTION_ARGS)
{
 int64  amount = PG_GETARG_INT64(0);
 Cash  result;
 int   fpoint;
 int64  scale;
 int   i;
 struct lconv *lconvert = PGLC_localeconv();

 /* see comments about frac_digits in cash_in() */
 fpoint = lconvert->frac_digits;
 if (fpoint < 0 || fpoint > 10)
  fpoint = 2;

 /* compute required scale factor */
 scale = 1;
 for (i = 0; i < fpoint; i++)
  scale *= 10;

 /* compute amount * scale, checking for overflow */
 result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
              Int64GetDatum(scale)));

 PG_RETURN_CASH(result);
}

Messung V0.5 in Prozent
C=94 H=93 G=93

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002