aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/datatype.sgml5
-rw-r--r--src/backend/utils/adt/cash.c16
-rw-r--r--src/test/regress/expected/money.out38
-rw-r--r--src/test/regress/sql/money.sql10
4 files changed, 61 insertions, 8 deletions
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 464ce83d30e..39bbd87e677 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -977,6 +977,11 @@ SELECT '52093.89'::money::numeric::float8;
</para>
<para>
+ Division of a <type>money</type> value by an integer value is performed
+ with truncation of the fractional part towards zero. To get a rounded
+ result, divide by a floating-point value, or cast the <type>money</type>
+ value to <type>numeric</> before dividing and back to <type>money</type>
+ afterwards. (The latter is preferable to avoid risking precision loss.)
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.
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index b336185df7e..d329efaa9a8 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -621,7 +621,7 @@ cash_mul_flt8(PG_FUNCTION_ARGS)
float8 f = PG_GETARG_FLOAT8(1);
Cash result;
- result = c * f;
+ result = rint(c * f);
PG_RETURN_CASH(result);
}
@@ -636,7 +636,7 @@ flt8_mul_cash(PG_FUNCTION_ARGS)
Cash c = PG_GETARG_CASH(1);
Cash result;
- result = f * c;
+ result = rint(f * c);
PG_RETURN_CASH(result);
}
@@ -671,7 +671,7 @@ cash_mul_flt4(PG_FUNCTION_ARGS)
float4 f = PG_GETARG_FLOAT4(1);
Cash result;
- result = c * f;
+ result = rint(c * (float8) f);
PG_RETURN_CASH(result);
}
@@ -686,7 +686,7 @@ flt4_mul_cash(PG_FUNCTION_ARGS)
Cash c = PG_GETARG_CASH(1);
Cash result;
- result = f * c;
+ result = rint((float8) f * c);
PG_RETURN_CASH(result);
}
@@ -707,7 +707,7 @@ cash_div_flt4(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- result = rint(c / f);
+ result = rint(c / (float8) f);
PG_RETURN_CASH(result);
}
@@ -756,7 +756,7 @@ cash_div_int8(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- result = rint(c / i);
+ result = c / i;
PG_RETURN_CASH(result);
}
@@ -808,7 +808,7 @@ cash_div_int4(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- result = rint(c / i);
+ result = c / i;
PG_RETURN_CASH(result);
}
@@ -858,7 +858,7 @@ cash_div_int2(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
- result = rint(c / s);
+ result = c / s;
PG_RETURN_CASH(result);
}
diff --git a/src/test/regress/expected/money.out b/src/test/regress/expected/money.out
index 538235c4cc2..efb48f1acd6 100644
--- a/src/test/regress/expected/money.out
+++ b/src/test/regress/expected/money.out
@@ -185,6 +185,44 @@ SELECT * FROM money_data;
$123.46
(1 row)
+-- rounding vs. truncation in division
+SELECT '878.08'::money / 11::float8;
+ ?column?
+----------
+ $79.83
+(1 row)
+
+SELECT '878.08'::money / 11::float4;
+ ?column?
+----------
+ $79.83
+(1 row)
+
+SELECT '878.08'::money / 11::int;
+ ?column?
+----------
+ $79.82
+(1 row)
+
+SELECT '878.08'::money / 11::smallint;
+ ?column?
+----------
+ $79.82
+(1 row)
+
+-- check for precision loss in division
+SELECT '90000000000000099.00'::money / 10::int;
+ ?column?
+---------------------------
+ $9,000,000,000,000,009.90
+(1 row)
+
+SELECT '90000000000000099.00'::money / 10::smallint;
+ ?column?
+---------------------------
+ $9,000,000,000,000,009.90
+(1 row)
+
-- Cast int4/int8 to money
SELECT 1234567890::money;
money
diff --git a/src/test/regress/sql/money.sql b/src/test/regress/sql/money.sql
index 09b9476b706..f0db5fa4322 100644
--- a/src/test/regress/sql/money.sql
+++ b/src/test/regress/sql/money.sql
@@ -57,6 +57,16 @@ DELETE FROM money_data;
INSERT INTO money_data VALUES ('$123.459');
SELECT * FROM money_data;
+-- rounding vs. truncation in division
+SELECT '878.08'::money / 11::float8;
+SELECT '878.08'::money / 11::float4;
+SELECT '878.08'::money / 11::int;
+SELECT '878.08'::money / 11::smallint;
+
+-- check for precision loss in division
+SELECT '90000000000000099.00'::money / 10::int;
+SELECT '90000000000000099.00'::money / 10::smallint;
+
-- Cast int4/int8 to money
SELECT 1234567890::money;
SELECT 12345678901234567::money;