summaryrefslogtreecommitdiffstats
path: root/Modules/mathmodule.c
diff options
context:
space:
mode:
authorRaymond Hettinger <rhettinger@users.noreply.github.com>2018-08-29 05:47:24 (GMT)
committerGitHub <noreply@github.com>2018-08-29 05:47:24 (GMT)
commit21786f5186383e8912e761eccd0f4ac1cca83217 (patch)
tree1f198cca94a71a95c876d5ebe5f2feefe62930d1 /Modules/mathmodule.c
parent124b9eb4e41ba1676dad954eec9a1fb3480794e4 (diff)
downloadcpython-21786f5186383e8912e761eccd0f4ac1cca83217.zip
cpython-21786f5186383e8912e761eccd0f4ac1cca83217.tar.gz
cpython-21786f5186383e8912e761eccd0f4ac1cca83217.tar.bz2
Improve commutativity of math.hypot() and math.dist() (GH-8984)
Diffstat (limited to 'Modules/mathmodule.c')
-rw-r--r--Modules/mathmodule.c32
1 files changed, 19 insertions, 13 deletions
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 62d3279..37934f6 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -2037,26 +2037,32 @@ where *max* is the largest value in the vector, compute:
max * sqrt(sum((x / max) ** 2 for x in vec))
-When a maximum value is found, it is swapped to the end. This
-lets us skip one loop iteration and just add 1.0 at the end.
-Saving the largest value for last also helps improve accuracy.
-
-Kahan summation is used to improve accuracy. The *csum*
-variable tracks the cumulative sum and *frac* tracks
-fractional round-off error for the most recent addition.
-
The value of the *max* variable must be present in *vec*
or should equal to 0.0 when n==0. Likewise, *max* will
be INF if an infinity is present in the vec.
The *found_nan* variable indicates whether some member of
the *vec* is a NaN.
+
+To improve accuracy and to increase the number of cases where
+vector_norm() is commutative, we use a variant of Neumaier
+summation specialized to exploit that we always know that
+|csum| >= |x|.
+
+The *csum* variable tracks the cumulative sum and *frac* tracks
+the cumulative fractional errors at each step. Since this
+variant assumes that |csum| >= |x| at each step, we establish
+the precondition by starting the accumulation from 1.0 which
+represents an entry equal to *max*. This also provides a nice
+side benefit in that it lets us skip over a *max* entry (which
+is swapped into *last*) saving us one iteration through the loop.
+
*/
static inline double
vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
{
- double x, csum = 0.0, oldcsum, frac = 0.0, last;
+ double x, csum = 1.0, oldcsum, frac = 0.0, last;
Py_ssize_t i;
if (Py_IS_INFINITY(max)) {
@@ -2078,14 +2084,14 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
last = max;
}
x /= max;
- x = x*x - frac;
+ x = x*x;
+ assert(csum >= x);
oldcsum = csum;
csum += x;
- frac = (csum - oldcsum) - x;
+ frac += (oldcsum - csum) + x;
}
assert(last == max);
- csum += 1.0 - frac;
- return max * sqrt(csum);
+ return max * sqrt(csum + frac);
}
#define NUM_STACK_ELEMS 16