*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 843,865 **** ALTER SEQUENCE <replaceable class="parameter">tablename</replaceable>_<replaceab
      floating-point literals, as well as typical
      currency formatting, such as <literal>'$1,000.00'</literal>.
      Output is generally in the latter form but depends on the locale.
!     Non-quoted numeric values can be converted to <type>money</type> by
!     casting the numeric value to <type>text</type> and then
!     <type>money</type>, for example:
  <programlisting>
! SELECT 1234::text::money;
  </programlisting>
!     There is no simple way of doing the reverse in a locale-independent
!     manner, namely casting a <type>money</type> value to a numeric type.
!     If you know the currency symbol and thousands separator you can use
!     <function>regexp_replace()</>:
  <programlisting>
! SELECT regexp_replace('52093.89'::money::text, '[$,]', '', 'g')::numeric;
  </programlisting>
  
     </para>
  
     <para>
      Since the output of this data type is locale-sensitive, it might not
      work to load <type>money</> data into a database that has a different
      setting of <varname>lc_monetary</>.  To avoid problems, before
--- 843,873 ----
      floating-point literals, as well as typical
      currency formatting, such as <literal>'$1,000.00'</literal>.
      Output is generally in the latter form but depends on the locale.
!    </para>
! 
!    <para>
!     Values of the <type>numeric</type> data type can be cast to <type>money</type>.
! 	Other numeric types can be converted to <type>money</type> by casting to
! 	<type>numeric</type> first, for example:
  <programlisting>
! SELECT 1234::numeric::money;
  </programlisting>
! 	A <type>money</type> value can be cast to <type>numeric</type> without
! 	loss of precision. Conversion to other types could potentially lose precision,
! 	and it must be done in two stages:
  <programlisting>
! SELECT '52093.89'::money::numeric::float;
  </programlisting>
  
     </para>
  
     <para>
+     When a <type>money</type> value is divided by another <type>money</type> value,
+ 	the result is <type>double precision</type> (i.e. a pure number, not money);
+ 	the currency units cancel each other out in the division.
+    </para>
+ 
+    <para>
      Since the output of this data type is locale-sensitive, it might not
      work to load <type>money</> data into a database that has a different
      setting of <varname>lc_monetary</>.  To avoid problems, before
*** a/src/backend/utils/adt/cash.c
--- b/src/backend/utils/adt/cash.c
***************
*** 27,32 ****
--- 27,33 ----
  #include "utils/builtins.h"
  #include "utils/cash.h"
  #include "utils/pg_locale.h"
+ #include "utils/numeric.h"
  
  #define CASH_BUFSZ		36
  
***************
*** 845,847 **** cash_words(PG_FUNCTION_ARGS)
--- 846,941 ----
  	/* return as text datum */
  	PG_RETURN_TEXT_P(cstring_to_text(buf));
  }
+ 
+ /* 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_numeric()
+  * Convert cash to numeric.
+  */
+ Datum
+ cash_numeric(PG_FUNCTION_ARGS)
+ {
+ 	Cash		money = PG_GETARG_CASH(0);
+ 	int			fpoint;
+ 	int64		scale;
+ 	int			i;
+ 	Numeric		result;
+ 	Datum		amount;
+ 	Datum		numeric_scale;
+ 	Datum		one;
+ 
+ 	struct lconv *lconvert = PGLC_localeconv();
+ 
+ 	/*
+ 	 * Find the number of digits after the decimal point. (These lines were
+ 	 * copied from cash_in().)
+ 	 */
+ 	fpoint = lconvert->frac_digits;
+ 	if (fpoint < 0 || fpoint > 10)
+ 		fpoint = 2;
+ 	scale = 1;
+ 	for (i = 0; i < fpoint; i++)
+ 		scale *= 10;
+ 
+ 	amount = DirectFunctionCall1(&int8_numeric, Int64GetDatum(money));
+ 	one = DirectFunctionCall1(&int8_numeric, Int64GetDatum(1));
+ 	numeric_scale = DirectFunctionCall1(&int8_numeric, Int64GetDatum(scale));
+ 	numeric_scale = DirectFunctionCall2(&numeric_div, one, numeric_scale);
+ 	result = DatumGetNumeric(DirectFunctionCall2(&numeric_mul, amount, numeric_scale));
+ 
+ 	result->n_sign_dscale = NUMERIC_SIGN(result) | fpoint;		/* Display the right
+ 																 * number of decimal
+ 																 * digits. */
+ 
+ 	PG_RETURN_NUMERIC(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();
+ 
+ 	/*
+ 	 * Find the number of digits after the decimal point.
+ 	 */
+ 	fpoint = lconvert->frac_digits;
+ 	if (fpoint < 0 || fpoint > 10)
+ 		fpoint = 2;
+ 	scale = 1;
+ 	for (i = 0; i < fpoint; i++)
+ 		scale *= 10;
+ 
+ 	numeric_scale = DirectFunctionCall1(&int8_numeric, Int64GetDatum(scale));
+ 	amount = DirectFunctionCall2(&numeric_mul, amount, numeric_scale);
+ 	amount = DirectFunctionCall1(&numeric_int8, amount);
+ 
+ 	result = DatumGetInt64(amount);
+ 	PG_RETURN_CASH(result);
+ }
*** a/src/include/catalog/pg_cast.h
--- b/src/include/catalog/pg_cast.h
***************
*** 124,129 **** DATA(insert ( 1700	 21 1783 a f ));
--- 124,131 ----
  DATA(insert ( 1700	 23 1744 a f ));
  DATA(insert ( 1700	700 1745 i f ));
  DATA(insert ( 1700	701 1746 i f ));
+ DATA(insert (  790 1700 3823 a f ));
+ DATA(insert ( 1700  790 3824 a f ));
  
  /* Allow explicit coercions between int4 and bool */
  DATA(insert (	23	16	2557 e f ));
*** a/src/include/catalog/pg_operator.h
--- b/src/include/catalog/pg_operator.h
***************
*** 415,420 **** DATA(insert OID = 915 (  "/"	   PGNSP PGUID b f f	790  21		790   0   0 cash_div_
--- 415,421 ----
  DATA(insert OID = 916 (  "*"	   PGNSP PGUID b f f	701  790	790 908   0 flt8_mul_cash - - ));
  DATA(insert OID = 917 (  "*"	   PGNSP PGUID b f f	23	790		790 912   0 int4_mul_cash - - ));
  DATA(insert OID = 918 (  "*"	   PGNSP PGUID b f f	21	790		790 914   0 int2_mul_cash - - ));
+ DATA(insert OID = 3825 (  "/"	   PGNSP PGUID b f f    790  790	701   0   0 cash_div_cash - - ));
  
  DATA(insert OID = 965 (  "^"	   PGNSP PGUID b f f	701  701	701 0 0 dpow - - ));
  DATA(insert OID = 966 (  "+"	   PGNSP PGUID b f f 1034 1033 1034 0 0 aclinsert - - ));
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 1197,1202 **** DATA(insert OID =  919 (  flt8_mul_cash    PGNSP PGUID 12 1 0 0 f f f t f i 2 0
--- 1197,1208 ----
  DESCR("multiply");
  DATA(insert OID =  935 (  cash_words	   PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "790" _null_ _null_ _null_ _null_	cash_words _null_ _null_ _null_ ));
  DESCR("output amount as words");
+ DATA(insert OID = 3822 (  cash_div_cash    PGNSP PGUID 12 1 0 0 f f f t f i 2 0 701 "790 790" _null_ _null_ _null_ _null_	cash_div_cash _null_ _null_ _null_ ));
+ DESCR("divide");
+ DATA(insert OID = 3823 (  numeric          PGNSP PGUID 12 1 0 0 f f f t f i 1 0 1700 "790" _null_ _null_ _null_ _null_      cash_numeric _null_ _null_ _null_ ));
+ DESCR("(internal)");
+ DATA(insert OID = 3824 (  money            PGNSP PGUID 12 1 0 0 f f f t f i 1 0 790 "1700" _null_ _null_ _null_ _null_      numeric_cash _null_ _null_ _null_ ));
+ DESCR("(internal)");
  
  /* OIDS 900 - 999 */
  
*** a/src/include/utils/cash.h
--- b/src/include/utils/cash.h
***************
*** 63,66 **** extern Datum cashsmaller(PG_FUNCTION_ARGS);
--- 63,70 ----
  
  extern Datum cash_words(PG_FUNCTION_ARGS);
  
+ extern Datum cash_div_cash(PG_FUNCTION_ARGS);
+ extern Datum cash_numeric(PG_FUNCTION_ARGS);
+ extern Datum numeric_cash(PG_FUNCTION_ARGS);
+ 
  #endif   /* CASH_H */
