Commit a0fad976 authored by Tom Lane's avatar Tom Lane

Re-implement division for numeric values using the traditional "schoolbook"

algorithm.  This is a good deal slower than our old roundoff-error-prone
code for long inputs, so we keep the old code for use in the transcendental
functions, where everything is approximate anyway.  Also create a
user-accessible function div(numeric, numeric) to provide access to the
exact result of trunc(x/y) --- since the regular numeric / operator will
round off its result, simply computing that expression in SQL doesn't
reliably give the desired answer.  This fixes bug #3387 and various related
corner cases, and improves the usefulness of PG for high-precision integer
arithmetic.
parent b6f0ad4b
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.426 2008/04/04 16:57:21 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.427 2008/04/04 18:45:36 tgl Exp $ -->
<chapter id="functions">
<title>Functions and Operators</title>
......@@ -617,6 +617,9 @@
<indexterm>
<primary>degrees</primary>
</indexterm>
<indexterm>
<primary>div</primary>
</indexterm>
<indexterm>
<primary>exp</primary>
</indexterm>
......@@ -717,6 +720,15 @@
<entry><literal>28.6478897565412</literal></entry>
</row>
<row>
<entry><literal><function>div</function>(<parameter>y</parameter> <type>numeric</>,
<parameter>x</parameter> <type>numeric</>)</literal></entry>
<entry><type>numeric</></entry>
<entry>integer quotient of <parameter>y</parameter>/<parameter>x</parameter></entry>
<entry><literal>div(9,4)</literal></entry>
<entry><literal>2</literal></entry>
</row>
<row>
<entry><literal><function>exp</function>(<type>dp</type> or <type>numeric</type>)</literal></entry>
<entry>(same as input)</entry>
......
This diff is collapsed.
......@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.444 2008/03/23 00:24:19 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.445 2008/04/04 18:45:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200803222
#define CATALOG_VERSION_NO 200804041
#endif
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.486 2008/04/04 16:57:21 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.487 2008/04/04 18:45:36 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
......@@ -1115,7 +1115,7 @@ DESCR("does not match LIKE expression");
DATA(insert OID = 860 ( bpchar PGNSP PGUID 12 1 0 f f t f i 1 1042 "18" _null_ _null_ _null_ char_bpchar - _null_ _null_ ));
DESCR("convert char to char()");
DATA(insert OID = 861 ( current_database PGNSP PGUID 12 1 0 f f t f i 0 19 "" _null_ _null_ _null_ current_database - _null_ _null_ ));
DATA(insert OID = 861 ( current_database PGNSP PGUID 12 1 0 f f t f s 0 19 "" _null_ _null_ _null_ current_database - _null_ _null_ ));
DESCR("returns the current database");
DATA(insert OID = 817 ( current_query PGNSP PGUID 12 1 0 f f f f v 0 25 "" _null_ _null_ _null_ current_query - _null_ _null_ ));
DESCR("returns the currently executing query");
......@@ -2573,6 +2573,10 @@ DATA(insert OID = 1745 ( float4 PGNSP PGUID 12 1 0 f f t f i 1 700 "1700" _n
DESCR("(internal)");
DATA(insert OID = 1746 ( float8 PGNSP PGUID 12 1 0 f f t f i 1 701 "1700" _null_ _null_ _null_ numeric_float8 - _null_ _null_ ));
DESCR("(internal)");
DATA(insert OID = 1973 ( div PGNSP PGUID 12 1 0 f f t f i 2 1700 "1700 1700" _null_ _null_ _null_ numeric_div_trunc - _null_ _null_ ));
DESCR("trunc(x/y)");
DATA(insert OID = 1980 ( numeric_div_trunc PGNSP PGUID 12 1 0 f f t f i 2 1700 "1700 1700" _null_ _null_ _null_ numeric_div_trunc - _null_ _null_ ));
DESCR("trunc(x/y)");
DATA(insert OID = 2170 ( width_bucket PGNSP PGUID 12 1 0 f f t f i 4 23 "1700 1700 1700 23" _null_ _null_ _null_ width_bucket_numeric - _null_ _null_ ));
DESCR("bucket number of operand in equidepth histogram");
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.311 2008/04/04 16:57:21 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.312 2008/04/04 18:45:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -845,6 +845,7 @@ extern Datum numeric_add(PG_FUNCTION_ARGS);
extern Datum numeric_sub(PG_FUNCTION_ARGS);
extern Datum numeric_mul(PG_FUNCTION_ARGS);
extern Datum numeric_div(PG_FUNCTION_ARGS);
extern Datum numeric_div_trunc(PG_FUNCTION_ARGS);
extern Datum numeric_mod(PG_FUNCTION_ARGS);
extern Datum numeric_inc(PG_FUNCTION_ARGS);
extern Datum numeric_smaller(PG_FUNCTION_ARGS);
......
......@@ -1260,3 +1260,84 @@ SELECT * FROM num_input_test;
-555.50
(5 rows)
--
-- Test some corner cases for division
--
select 999999999999999999999::numeric/1000000000000000000000;
?column?
------------------------
1.00000000000000000000
(1 row)
select div(999999999999999999999::numeric,1000000000000000000000);
div
-----
0
(1 row)
select mod(999999999999999999999::numeric,1000000000000000000000);
mod
-----------------------
999999999999999999999
(1 row)
select div(-9999999999999999999999::numeric,1000000000000000000000);
div
-----
-9
(1 row)
select mod(-9999999999999999999999::numeric,1000000000000000000000);
mod
------------------------
-999999999999999999999
(1 row)
select div(-9999999999999999999999::numeric,1000000000000000000000)*1000000000000000000000 + mod(-9999999999999999999999::numeric,1000000000000000000000);
?column?
-------------------------
-9999999999999999999999
(1 row)
select mod (70.0,70) ;
mod
-----
0.0
(1 row)
select div (70.0,70) ;
div
-----
1
(1 row)
select 70.0 / 70 ;
?column?
------------------------
1.00000000000000000000
(1 row)
select 12345678901234567890 % 123;
?column?
----------
78
(1 row)
select 12345678901234567890 / 123;
?column?
--------------------
100371373180768845
(1 row)
select div(12345678901234567890, 123);
div
--------------------
100371373180768844
(1 row)
select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123;
?column?
----------------------
12345678901234567890
(1 row)
......@@ -805,3 +805,21 @@ INSERT INTO num_input_test(n1) VALUES ('');
INSERT INTO num_input_test(n1) VALUES (' N aN ');
SELECT * FROM num_input_test;
--
-- Test some corner cases for division
--
select 999999999999999999999::numeric/1000000000000000000000;
select div(999999999999999999999::numeric,1000000000000000000000);
select mod(999999999999999999999::numeric,1000000000000000000000);
select div(-9999999999999999999999::numeric,1000000000000000000000);
select mod(-9999999999999999999999::numeric,1000000000000000000000);
select div(-9999999999999999999999::numeric,1000000000000000000000)*1000000000000000000000 + mod(-9999999999999999999999::numeric,1000000000000000000000);
select mod (70.0,70) ;
select div (70.0,70) ;
select 70.0 / 70 ;
select 12345678901234567890 % 123;
select 12345678901234567890 / 123;
select div(12345678901234567890, 123);
select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment