*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
***************
*** 843,863 **** 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
--- 843,871 ----
      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
*** 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,939 ----
  	/* 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
***************
*** 1195,1202 **** DATA(insert OID =  899 (  cashsmaller	   PGNSP PGUID 12 1 0 0 f f f t f i 2 0 79
  DESCR("smaller of two");
  DATA(insert OID =  919 (  flt8_mul_cash    PGNSP PGUID 12 1 0 0 f f f t f i 2 0 790 "701 790" _null_ _null_ _null_ _null_	flt8_mul_cash _null_ _null_ _null_ ));
  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");
  
  /* OIDS 900 - 999 */
  
--- 1195,1208 ----
  DESCR("smaller of two");
  DATA(insert OID =  919 (  flt8_mul_cash    PGNSP PGUID 12 1 0 0 f f f t f i 2 0 790 "701 790" _null_ _null_ _null_ _null_	flt8_mul_cash _null_ _null_ _null_ ));
  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 */
