Commit 4083f445 authored by Dean Rasheed's avatar Dean Rasheed

Improve the performance and accuracy of numeric sqrt() and ln().

Instead of using Newton's method to compute numeric square roots, use
the Karatsuba square root algorithm, which performs better for numbers
of all sizes. In practice, this is 3-5 times faster for inputs with
just a few digits and up to around 10 times faster for larger inputs.

Also, the new algorithm guarantees that the final digit of the result
is correctly rounded, since it computes an integer square root with
truncation, containing at least 1 extra decimal digit before rounding.
The former algorithm would occasionally round the wrong way because
it rounded both the intermediate and final results.

In addition, arrange for sqrt_var() to explicitly support negative
rscale values (rounding before the decimal point). This allows the
argument reduction phase of ln_var() to be optimised for large inputs,
since it only needs to compute square roots with a few more digits
than the final ln() result, rather than computing all the digits
before the decimal point. For very large inputs, this can be many
thousands of times faster.

In passing, optimise div_var_fast() in a couple of places where it was
doing unnecessary work.

Patch be me, reviewed by Tom Lane and Tels.

Discussion: https://postgr.es/m/CAEZATCV1A7+jD3P30Zu31KjaxeSEyOn3v9d6tYegpxcq3cQu-g@mail.gmail.com
parent 8f3ec75d
This diff is collapsed.
...@@ -1579,6 +1579,57 @@ select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123; ...@@ -1579,6 +1579,57 @@ select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123;
12345678901234567890 12345678901234567890
(1 row) (1 row)
--
-- Test some corner cases for square root
--
select sqrt(1.000000000000003::numeric);
sqrt
-------------------
1.000000000000001
(1 row)
select sqrt(1.000000000000004::numeric);
sqrt
-------------------
1.000000000000002
(1 row)
select sqrt(96627521408608.56340355805::numeric);
sqrt
---------------------
9829929.87811248648
(1 row)
select sqrt(96627521408608.56340355806::numeric);
sqrt
---------------------
9829929.87811248649
(1 row)
select sqrt(515549506212297735.073688290367::numeric);
sqrt
------------------------
718017761.766585921184
(1 row)
select sqrt(515549506212297735.073688290368::numeric);
sqrt
------------------------
718017761.766585921185
(1 row)
select sqrt(8015491789940783531003294973900306::numeric);
sqrt
-------------------
89529278953540017
(1 row)
select sqrt(8015491789940783531003294973900307::numeric);
sqrt
-------------------
89529278953540018
(1 row)
-- --
-- Test code path for raising to integer powers -- Test code path for raising to integer powers
-- --
......
...@@ -882,6 +882,19 @@ select 12345678901234567890 / 123; ...@@ -882,6 +882,19 @@ select 12345678901234567890 / 123;
select div(12345678901234567890, 123); select div(12345678901234567890, 123);
select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123; select div(12345678901234567890, 123) * 123 + 12345678901234567890 % 123;
--
-- Test some corner cases for square root
--
select sqrt(1.000000000000003::numeric);
select sqrt(1.000000000000004::numeric);
select sqrt(96627521408608.56340355805::numeric);
select sqrt(96627521408608.56340355806::numeric);
select sqrt(515549506212297735.073688290367::numeric);
select sqrt(515549506212297735.073688290368::numeric);
select sqrt(8015491789940783531003294973900306::numeric);
select sqrt(8015491789940783531003294973900307::numeric);
-- --
-- Test code path for raising to integer powers -- Test code path for raising to integer powers
-- --
......
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