Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 56 additions & 41 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2032,10 +2032,10 @@ math_fmod_impl(PyObject *module, double x, double y)
}

/*
Given an *n* length *vec* of non-negative, non-nan, non-inf values
Given an *n* length *vec* of non-negative values
where *max* is the largest value in the vector, compute:

sum((x / max) ** 2 for x in vec)
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.
Expand All @@ -2045,19 +2045,31 @@ 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.
*/

static inline double
scaled_vector_squared(Py_ssize_t n, double *vec, double max)
vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
{
double x, csum = 0.0, oldcsum, frac = 0.0;
Py_ssize_t i;

if (Py_IS_INFINITY(max)) {
return max;
}
if (found_nan) {
return Py_NAN;
}
if (max == 0.0) {
return 0.0;
}
assert(n > 0);
for (i=0 ; i<n-1 ; i++) {
for (i=0 ; i < n-1 ; i++) {
x = vec[i];
if (x == max) {
x = vec[n-1];
Expand All @@ -2071,9 +2083,11 @@ scaled_vector_squared(Py_ssize_t n, double *vec, double max)
}
assert(vec[n-1] == max);
csum += 1.0 - frac;
return csum;
return max * sqrt(csum);
}

#define NUM_STACK_ELEMS 16

/*[clinic input]
math.dist

Expand All @@ -2095,11 +2109,12 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
/*[clinic end generated code: output=56bd9538d06bbcfe input=937122eaa5f19272]*/
{
PyObject *item;
double *diffs;
double max = 0.0;
double x, px, qx, result;
Py_ssize_t i, m, n;
int found_nan = 0;
double diffs_on_stack[NUM_STACK_ELEMS];
double *diffs = diffs_on_stack;

m = PyTuple_GET_SIZE(p);
n = PyTuple_GET_SIZE(q);
Expand All @@ -2109,22 +2124,22 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
return NULL;

}
diffs = (double *) PyObject_Malloc(n * sizeof(double));
if (diffs == NULL) {
return NULL;
if (n > NUM_STACK_ELEMS) {
diffs = (double *) PyObject_Malloc(n * sizeof(double));
if (diffs == NULL) {
return NULL;
}
}
for (i=0 ; i<n ; i++) {
item = PyTuple_GET_ITEM(p, i);
px = PyFloat_AsDouble(item);
if (px == -1.0 && PyErr_Occurred()) {
PyObject_Free(diffs);
return NULL;
goto error_exit;
}
item = PyTuple_GET_ITEM(q, i);
qx = PyFloat_AsDouble(item);
if (qx == -1.0 && PyErr_Occurred()) {
PyObject_Free(diffs);
return NULL;
goto error_exit;
}
x = fabs(px - qx);
diffs[i] = x;
Expand All @@ -2133,19 +2148,17 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
max = x;
}
}
if (Py_IS_INFINITY(max)) {
result = max;
goto done;
}
if (found_nan) {
result = Py_NAN;
goto done;
result = vector_norm(n, diffs, max, found_nan);
if (diffs != diffs_on_stack) {
PyObject_Free(diffs);
}
result = max * sqrt(scaled_vector_squared(n, diffs, max));

done:
PyObject_Free(diffs);
return PyFloat_FromDouble(result);

error_exit:
if (diffs != diffs_on_stack) {
PyObject_Free(diffs);
}
return NULL;
}

/* AC: cannot convert yet, waiting for *args support */
Expand All @@ -2154,21 +2167,23 @@ math_hypot(PyObject *self, PyObject *args)
{
Py_ssize_t i, n;
PyObject *item;
double *coordinates;
double max = 0.0;
double x, result;
int found_nan = 0;
double coord_on_stack[NUM_STACK_ELEMS];
double *coordinates = coord_on_stack;

n = PyTuple_GET_SIZE(args);
coordinates = (double *) PyObject_Malloc(n * sizeof(double));
if (coordinates == NULL)
return NULL;
if (n > NUM_STACK_ELEMS) {
coordinates = (double *) PyObject_Malloc(n * sizeof(double));
if (coordinates == NULL)
return NULL;
}
for (i=0 ; i<n ; i++) {
item = PyTuple_GET_ITEM(args, i);
x = PyFloat_AsDouble(item);
if (x == -1.0 && PyErr_Occurred()) {
PyObject_Free(coordinates);
return NULL;
goto error_exit;
}
x = fabs(x);
coordinates[i] = x;
Expand All @@ -2177,21 +2192,21 @@ math_hypot(PyObject *self, PyObject *args)
max = x;
}
}
if (Py_IS_INFINITY(max)) {
result = max;
goto done;
result = vector_norm(n, coordinates, max, found_nan);
if (coordinates != coord_on_stack) {
PyObject_Free(coordinates);
}
if (found_nan) {
result = Py_NAN;
goto done;
}
result = max * sqrt(scaled_vector_squared(n, coordinates, max));

done:
PyObject_Free(coordinates);
return PyFloat_FromDouble(result);

error_exit:
if (coordinates != coord_on_stack) {
PyObject_Free(coordinates);
}
return NULL;
}

#undef NUM_STACK_ELEMS

PyDoc_STRVAR(math_hypot_doc,
"hypot(*coordinates) -> value\n\n\
Multidimensional Euclidean distance from the origin to a point.\n\
Expand Down