@@ -2032,10 +2032,10 @@ math_fmod_impl(PyObject *module, double x, double y)
2032
2032
}
2033
2033
2034
2034
/*
2035
- Given an *n* length *vec* of non-negative, non-nan, non-inf values
2035
+ Given an *n* length *vec* of non-negative values
2036
2036
where *max* is the largest value in the vector, compute:
2037
2037
2038
- sum((x / max) ** 2 for x in vec)
2038
+ max * sqrt( sum((x / max) ** 2 for x in vec) )
2039
2039
2040
2040
When a maximum value is found, it is swapped to the end. This
2041
2041
lets us skip one loop iteration and just add 1.0 at the end.
@@ -2045,19 +2045,31 @@ Kahan summation is used to improve accuracy. The *csum*
2045
2045
variable tracks the cumulative sum and *frac* tracks
2046
2046
fractional round-off error for the most recent addition.
2047
2047
2048
+ The value of the *max* variable must be present in *vec*
2049
+ or should equal to 0.0 when n==0. Likewise, *max* will
2050
+ be INF if an infinity is present in the vec.
2051
+
2052
+ The *found_nan* variable indicates whether some member of
2053
+ the *vec* is a NaN.
2048
2054
*/
2049
2055
2050
2056
static inline double
2051
- scaled_vector_squared (Py_ssize_t n , double * vec , double max )
2057
+ vector_norm (Py_ssize_t n , double * vec , double max , int found_nan )
2052
2058
{
2053
2059
double x , csum = 0.0 , oldcsum , frac = 0.0 ;
2054
2060
Py_ssize_t i ;
2055
2061
2062
+ if (Py_IS_INFINITY (max )) {
2063
+ return max ;
2064
+ }
2065
+ if (found_nan ) {
2066
+ return Py_NAN ;
2067
+ }
2056
2068
if (max == 0.0 ) {
2057
2069
return 0.0 ;
2058
2070
}
2059
2071
assert (n > 0 );
2060
- for (i = 0 ; i < n - 1 ; i ++ ) {
2072
+ for (i = 0 ; i < n - 1 ; i ++ ) {
2061
2073
x = vec [i ];
2062
2074
if (x == max ) {
2063
2075
x = vec [n - 1 ];
@@ -2071,9 +2083,11 @@ scaled_vector_squared(Py_ssize_t n, double *vec, double max)
2071
2083
}
2072
2084
assert (vec [n - 1 ] == max );
2073
2085
csum += 1.0 - frac ;
2074
- return csum ;
2086
+ return max * sqrt ( csum ) ;
2075
2087
}
2076
2088
2089
+ #define NUM_STACK_ELEMS 16
2090
+
2077
2091
/*[clinic input]
2078
2092
math.dist
2079
2093
@@ -2095,11 +2109,12 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
2095
2109
/*[clinic end generated code: output=56bd9538d06bbcfe input=937122eaa5f19272]*/
2096
2110
{
2097
2111
PyObject * item ;
2098
- double * diffs ;
2099
2112
double max = 0.0 ;
2100
2113
double x , px , qx , result ;
2101
2114
Py_ssize_t i , m , n ;
2102
2115
int found_nan = 0 ;
2116
+ double diffs_on_stack [NUM_STACK_ELEMS ];
2117
+ double * diffs = diffs_on_stack ;
2103
2118
2104
2119
m = PyTuple_GET_SIZE (p );
2105
2120
n = PyTuple_GET_SIZE (q );
@@ -2109,22 +2124,22 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
2109
2124
return NULL ;
2110
2125
2111
2126
}
2112
- diffs = (double * ) PyObject_Malloc (n * sizeof (double ));
2113
- if (diffs == NULL ) {
2114
- return NULL ;
2127
+ if (n > NUM_STACK_ELEMS ) {
2128
+ diffs = (double * ) PyObject_Malloc (n * sizeof (double ));
2129
+ if (diffs == NULL ) {
2130
+ return NULL ;
2131
+ }
2115
2132
}
2116
2133
for (i = 0 ; i < n ; i ++ ) {
2117
2134
item = PyTuple_GET_ITEM (p , i );
2118
2135
px = PyFloat_AsDouble (item );
2119
2136
if (px == -1.0 && PyErr_Occurred ()) {
2120
- PyObject_Free (diffs );
2121
- return NULL ;
2137
+ goto error_exit ;
2122
2138
}
2123
2139
item = PyTuple_GET_ITEM (q , i );
2124
2140
qx = PyFloat_AsDouble (item );
2125
2141
if (qx == -1.0 && PyErr_Occurred ()) {
2126
- PyObject_Free (diffs );
2127
- return NULL ;
2142
+ goto error_exit ;
2128
2143
}
2129
2144
x = fabs (px - qx );
2130
2145
diffs [i ] = x ;
@@ -2133,19 +2148,17 @@ math_dist_impl(PyObject *module, PyObject *p, PyObject *q)
2133
2148
max = x ;
2134
2149
}
2135
2150
}
2136
- if (Py_IS_INFINITY (max )) {
2137
- result = max ;
2138
- goto done ;
2139
- }
2140
- if (found_nan ) {
2141
- result = Py_NAN ;
2142
- goto done ;
2151
+ result = vector_norm (n , diffs , max , found_nan );
2152
+ if (diffs != diffs_on_stack ) {
2153
+ PyObject_Free (diffs );
2143
2154
}
2144
- result = max * sqrt (scaled_vector_squared (n , diffs , max ));
2145
-
2146
- done :
2147
- PyObject_Free (diffs );
2148
2155
return PyFloat_FromDouble (result );
2156
+
2157
+ error_exit :
2158
+ if (diffs != diffs_on_stack ) {
2159
+ PyObject_Free (diffs );
2160
+ }
2161
+ return NULL ;
2149
2162
}
2150
2163
2151
2164
/* AC: cannot convert yet, waiting for *args support */
@@ -2154,21 +2167,23 @@ math_hypot(PyObject *self, PyObject *args)
2154
2167
{
2155
2168
Py_ssize_t i , n ;
2156
2169
PyObject * item ;
2157
- double * coordinates ;
2158
2170
double max = 0.0 ;
2159
2171
double x , result ;
2160
2172
int found_nan = 0 ;
2173
+ double coord_on_stack [NUM_STACK_ELEMS ];
2174
+ double * coordinates = coord_on_stack ;
2161
2175
2162
2176
n = PyTuple_GET_SIZE (args );
2163
- coordinates = (double * ) PyObject_Malloc (n * sizeof (double ));
2164
- if (coordinates == NULL )
2165
- return NULL ;
2177
+ if (n > NUM_STACK_ELEMS ) {
2178
+ coordinates = (double * ) PyObject_Malloc (n * sizeof (double ));
2179
+ if (coordinates == NULL )
2180
+ return NULL ;
2181
+ }
2166
2182
for (i = 0 ; i < n ; i ++ ) {
2167
2183
item = PyTuple_GET_ITEM (args , i );
2168
2184
x = PyFloat_AsDouble (item );
2169
2185
if (x == -1.0 && PyErr_Occurred ()) {
2170
- PyObject_Free (coordinates );
2171
- return NULL ;
2186
+ goto error_exit ;
2172
2187
}
2173
2188
x = fabs (x );
2174
2189
coordinates [i ] = x ;
@@ -2177,21 +2192,21 @@ math_hypot(PyObject *self, PyObject *args)
2177
2192
max = x ;
2178
2193
}
2179
2194
}
2180
- if ( Py_IS_INFINITY ( max )) {
2181
- result = max ;
2182
- goto done ;
2195
+ result = vector_norm ( n , coordinates , max , found_nan );
2196
+ if ( coordinates != coord_on_stack ) {
2197
+ PyObject_Free ( coordinates ) ;
2183
2198
}
2184
- if (found_nan ) {
2185
- result = Py_NAN ;
2186
- goto done ;
2187
- }
2188
- result = max * sqrt (scaled_vector_squared (n , coordinates , max ));
2189
-
2190
- done :
2191
- PyObject_Free (coordinates );
2192
2199
return PyFloat_FromDouble (result );
2200
+
2201
+ error_exit :
2202
+ if (coordinates != coord_on_stack ) {
2203
+ PyObject_Free (coordinates );
2204
+ }
2205
+ return NULL ;
2193
2206
}
2194
2207
2208
+ #undef NUM_STACK_ELEMS
2209
+
2195
2210
PyDoc_STRVAR (math_hypot_doc ,
2196
2211
"hypot(*coordinates) -> value\n\n\
2197
2212
Multidimensional Euclidean distance from the origin to a point.\n\
0 commit comments