Re: dividing money by money - Mailing list pgsql-bugs
From | Andy Balholm |
---|---|
Subject | Re: dividing money by money |
Date | |
Msg-id | 7D642D1E-18A0-42B7-8666-18A242955402@balholm.com Whole thread Raw |
In response to | Re: dividing money by money ("Kevin Grittner" <Kevin.Grittner@wicourts.gov>) |
Responses |
Re: dividing money by money
|
List | pgsql-bugs |
> Or I guess we could leave this as you've written it and add support > for a cast from money to numeric. I tried rewriting my function to use numeric, but I discovered that numeric= division is not exact. (Otherwise SELECT 1::numeric / 3::numeric would res= ult in an infinite loop.) So I went back to my float8 version and wrote a c= ast from money to numeric. Here is my C source code now: #include <postgres.h> #include <fmgr.h> #include <utils/cash.h> #include <utils/numeric.h> #include <utils/pg_locale.h> PG_MODULE_MAGIC; extern Datum int8_numeric(PG_FUNCTION_ARGS); extern Datum numeric_div(PG_FUNCTION_ARGS); extern Datum numeric_mul(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(cash_div_cash); /* cash_div_cash() * Divide cash by cash, returning float8. */ Datum cash_div_cash(PG_FUNCTION_ARGS) { Cash dividend =3D PG_GETARG_CASH(0); Cash divisor =3D PG_GETARG_CASH(1); float8 quotient; =20=20=20=20 if (divisor =3D=3D 0) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); =20=20=20=20 quotient =3D (float8)dividend / (float8)divisor; PG_RETURN_FLOAT8(quotient); } PG_FUNCTION_INFO_V1(cash_numeric); /* cash_numeric() * Convert cash to numeric. */ Datum cash_numeric(PG_FUNCTION_ARGS) { Cash money =3D PG_GETARG_CASH(0); int fpoint; int64 scale; int i; Numeric result; Datum amount; Datum numeric_scale; Datum one; =20=20=20=20 struct lconv *lconvert =3D PGLC_localeconv(); =20=20=20=20 /*=20 * Find the number of digits after the decimal point. * (These lines were copied from cash_in().) */ fpoint =3D lconvert->frac_digits; if (fpoint < 0 || fpoint > 10) fpoint =3D 2; scale =3D 1; for (i =3D 0; i < fpoint; i++)=20 scale *=3D 10; =20=20=20=20 amount =3D DirectFunctionCall1(&int8_numeric, Int64GetDatum(mone= y)); one =3D DirectFunctionCall1(&int8_numeric, Int64GetDatum(1)); numeric_scale =3D DirectFunctionCall1(&int8_numeric, Int64GetDatum(scal= e)); numeric_scale =3D DirectFunctionCall2(&numeric_div, one, numeric_scale); result =3D DatumGetNumeric(DirectFunctionCall2(&numeric_mul, amount, nu= meric_scale)); =20=20=20=20 result->n_sign_dscale =3D NUMERIC_SIGN(result) | fpoint; /* Display the= right number of decimal digits. */ =20=20=20=20 PG_RETURN_NUMERIC(result); } ---------------------------------------------------------------------------= --------- And here is the SQL it takes to load it: CREATE FUNCTION cash_div_cash(money, money) RETURNS double precision LANGUAGE c IMMUTABLE STRICT AS '$libdir/divide_money', 'cash_div_cash'; CREATE FUNCTION cash_numeric(money) RETURNS numeric LANGUAGE c IMMUTABLE STRICT AS '$libdir/divide_money', 'cash_numeric'; CREATE OPERATOR / ( PROCEDURE =3D cash_div_cash, LEFTARG =3D money, RIGHTARG =3D money ); CREATE CAST (money AS numeric) WITH FUNCTION cash_numeric(money) AS ASSIGNM= ENT;
pgsql-bugs by date: