Skip to content

Commit 6719365

Browse files
committed
Replace uses of long with more portable types
`long` is unportable, since its width depends a lot on the target platform: it’s 32-bit on 32 bit platforms and Windows, but 64-bit elsewhere. `long k` in `lambertw.h` is replaced with `int64_t`, since it can be any integer and is castable to a float. `long n` in the spherical Bessel functions is replaced with `uint64_t`. This change also fixes three bugs: + Previously, `int`s were used in loops up to `n`, but this is the wrong type. This is changed to use `uint64_t`. + `n + 1` was used liberally before, but this causes UB when `n` is its maximum value for the type. We instead cast to the float type before adding one, thus avoiding UB (or in the case of the new unsigned type, overflow). + `std::pow(-1, n)` was previously used – however, this is incorrect when `n` is a `long`, since it will be implicitly converted to an `int`. We replace these with a simple ternary to control the sign.
1 parent 550f51c commit 6719365

File tree

2 files changed

+25
-65
lines changed

2 files changed

+25
-65
lines changed

include/xsf/lambertw.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ namespace detail {
5454
return z * cevalpoly(num, 2, z) / cevalpoly(denom, 2, z);
5555
}
5656

57-
XSF_HOST_DEVICE inline std::complex<double> lambertw_asy(std::complex<double> z, long k) {
57+
XSF_HOST_DEVICE inline std::complex<double> lambertw_asy(std::complex<double> z, int64_t k) {
5858
/* Compute the W function using the first two terms of the
5959
* asymptotic series. See 4.20 in [1].
6060
*/
@@ -64,7 +64,7 @@ namespace detail {
6464

6565
} // namespace detail
6666

67-
XSF_HOST_DEVICE inline std::complex<double> lambertw(std::complex<double> z, long k, double tol) {
67+
XSF_HOST_DEVICE inline std::complex<double> lambertw(std::complex<double> z, int64_t k, double tol) {
6868
double absz;
6969
std::complex<double> w;
7070
std::complex<double> ew, wew, wewz, wn;
@@ -142,7 +142,7 @@ XSF_HOST_DEVICE inline std::complex<double> lambertw(std::complex<double> z, lon
142142
return {std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()};
143143
}
144144

145-
XSF_HOST_DEVICE inline std::complex<float> lambertw(std::complex<float> z, long k, float tol) {
145+
XSF_HOST_DEVICE inline std::complex<float> lambertw(std::complex<float> z, int64_t k, float tol) {
146146
return static_cast<std::complex<float>>(lambertw(static_cast<std::complex<double>>(z), k, static_cast<double>(tol))
147147
);
148148
}

include/xsf/sph_bessel.h

Lines changed: 22 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,11 @@ Translated to C++ by SciPy developers in 2024.
3434
namespace xsf {
3535

3636
template <typename T>
37-
T sph_bessel_j(long n, T x) {
37+
T sph_bessel_j(uint64_t n, T x) {
3838
if (std::isnan(x)) {
3939
return x;
4040
}
4141

42-
if (n < 0) {
43-
set_error("spherical_jn", SF_ERROR_DOMAIN, nullptr);
44-
return std::numeric_limits<T>::quiet_NaN();
45-
}
46-
4742
if ((x == std::numeric_limits<T>::infinity()) || (x == -std::numeric_limits<T>::infinity())) {
4843
return 0;
4944
}
@@ -71,7 +66,7 @@ T sph_bessel_j(long n, T x) {
7166
}
7267

7368
T sn;
74-
for (int i = 0; i < n - 1; ++i) {
69+
for (uint64_t i = 0; i < n - 1; ++i) {
7570
sn = (2 * i + 3) * s1 / x - s0;
7671
s0 = s1;
7772
s1 = sn;
@@ -85,16 +80,11 @@ T sph_bessel_j(long n, T x) {
8580
}
8681

8782
template <typename T>
88-
std::complex<T> sph_bessel_j(long n, std::complex<T> z) {
83+
std::complex<T> sph_bessel_j(uint64_t n, std::complex<T> z) {
8984
if (std::isnan(std::real(z)) || std::isnan(std::imag(z))) {
9085
return z;
9186
}
9287

93-
if (n < 0) {
94-
set_error("spherical_jn", SF_ERROR_DOMAIN, nullptr);
95-
return std::numeric_limits<T>::quiet_NaN();
96-
}
97-
9888
if (std::real(z) == std::numeric_limits<T>::infinity() || std::real(z) == -std::numeric_limits<T>::infinity()) {
9989
// https://dlmf.nist.gov/10.52.E3
10090
if (std::imag(z) == 0) {
@@ -121,7 +111,7 @@ std::complex<T> sph_bessel_j(long n, std::complex<T> z) {
121111
}
122112

123113
template <typename T>
124-
T sph_bessel_j_jac(long n, T z) {
114+
T sph_bessel_j_jac(uint64_t n, T z) {
125115
if (n == 0) {
126116
return -sph_bessel_j(1, z);
127117
}
@@ -136,25 +126,20 @@ T sph_bessel_j_jac(long n, T z) {
136126
}
137127

138128
// DLMF 10.51.2
139-
return sph_bessel_j(n - 1, z) - static_cast<T>(n + 1) * sph_bessel_j(n, z) / z;
129+
return sph_bessel_j(n - 1, z) - (static_cast<T>(n) + 1) * sph_bessel_j(n, z) / z;
140130
}
141131

142132
template <typename T>
143-
T sph_bessel_y(long n, T x) {
133+
T sph_bessel_y(uint64_t n, T x) {
144134
T s0, s1, sn;
145-
int idx;
135+
uint64_t idx;
146136

147137
if (std::isnan(x)) {
148138
return x;
149139
}
150140

151-
if (n < 0) {
152-
set_error("spherical_yn", SF_ERROR_DOMAIN, nullptr);
153-
return std::numeric_limits<T>::quiet_NaN();
154-
}
155-
156141
if (x < 0) {
157-
return std::pow(-1, n + 1) * sph_bessel_y(n, -x);
142+
return n % 2 == 0 ? -sph_bessel_y(n, -x) : sph_bessel_y(n, -x);
158143
}
159144

160145
if (x == std::numeric_limits<T>::infinity() || x == -std::numeric_limits<T>::infinity()) {
@@ -188,19 +173,14 @@ T sph_bessel_y(long n, T x) {
188173
return sn;
189174
}
190175

191-
inline float sph_bessel_y(long n, float x) { return sph_bessel_y(n, static_cast<double>(x)); }
176+
inline float sph_bessel_y(uint64_t n, float x) { return sph_bessel_y(n, static_cast<double>(x)); }
192177

193178
template <typename T>
194-
std::complex<T> sph_bessel_y(long n, std::complex<T> z) {
179+
std::complex<T> sph_bessel_y(uint64_t n, std::complex<T> z) {
195180
if (std::isnan(std::real(z)) || std::isnan(std::imag(z))) {
196181
return z;
197182
}
198183

199-
if (n < 0) {
200-
set_error("spherical_yn", SF_ERROR_DOMAIN, nullptr);
201-
return std::numeric_limits<T>::quiet_NaN();
202-
}
203-
204184
if (std::real(z) == 0 && std::imag(z) == 0) {
205185
// https://dlmf.nist.gov/10.52.E2
206186
return std::numeric_limits<T>::quiet_NaN();
@@ -219,25 +199,20 @@ std::complex<T> sph_bessel_y(long n, std::complex<T> z) {
219199
}
220200

221201
template <typename T>
222-
T sph_bessel_y_jac(long n, T x) {
202+
T sph_bessel_y_jac(uint64_t n, T x) {
223203
if (n == 0) {
224204
return -sph_bessel_y(1, x);
225205
}
226206

227-
return sph_bessel_y(n - 1, x) - static_cast<T>(n + 1) * sph_bessel_y(n, x) / x;
207+
return sph_bessel_y(n - 1, x) - (static_cast<T>(n) + 1) * sph_bessel_y(n, x) / x;
228208
}
229209

230210
template <typename T>
231-
T sph_bessel_i(long n, T x) {
211+
T sph_bessel_i(uint64_t n, T x) {
232212
if (std::isnan(x)) {
233213
return x;
234214
}
235215

236-
if (n < 0) {
237-
set_error("spherical_in", SF_ERROR_DOMAIN, nullptr);
238-
return std::numeric_limits<T>::quiet_NaN();
239-
}
240-
241216
if (x == 0) {
242217
// https://dlmf.nist.gov/10.52.E1
243218
if (n == 0) {
@@ -249,7 +224,7 @@ T sph_bessel_i(long n, T x) {
249224
if (std::isinf(x)) {
250225
// https://dlmf.nist.gov/10.49.E8
251226
if (x == -std::numeric_limits<T>::infinity()) {
252-
return std::pow(-1, n) * std::numeric_limits<T>::infinity();
227+
return n % 2 == 0 ? std::numeric_limits<T>::infinity() : -std::numeric_limits<T>::infinity();
253228
}
254229

255230
return std::numeric_limits<T>::infinity();
@@ -259,16 +234,11 @@ T sph_bessel_i(long n, T x) {
259234
}
260235

261236
template <typename T>
262-
std::complex<T> sph_bessel_i(long n, std::complex<T> z) {
237+
std::complex<T> sph_bessel_i(uint64_t n, std::complex<T> z) {
263238
if (std::isnan(std::real(z)) || std::isnan(std::imag(z))) {
264239
return z;
265240
}
266241

267-
if (n < 0) {
268-
set_error("spherical_in", SF_ERROR_DOMAIN, nullptr);
269-
return std::numeric_limits<T>::quiet_NaN();
270-
}
271-
272242
if (std::abs(z) == 0) {
273243
// https://dlmf.nist.gov/10.52.E1
274244
if (n == 0) {
@@ -282,7 +252,7 @@ std::complex<T> sph_bessel_i(long n, std::complex<T> z) {
282252
// https://dlmf.nist.gov/10.52.E5
283253
if (std::imag(z) == 0) {
284254
if (std::real(z) == -std::numeric_limits<T>::infinity()) {
285-
return std::pow(-1, n) * std::numeric_limits<T>::infinity();
255+
return n % 2 == 0 ? std::numeric_limits<T>::infinity() : -std::numeric_limits<T>::infinity();
286256
}
287257

288258
return std::numeric_limits<T>::infinity();
@@ -295,7 +265,7 @@ std::complex<T> sph_bessel_i(long n, std::complex<T> z) {
295265
}
296266

297267
template <typename T>
298-
T sph_bessel_i_jac(long n, T z) {
268+
T sph_bessel_i_jac(uint64_t n, T z) {
299269
if (n == 0) {
300270
return sph_bessel_i(1, z);
301271
}
@@ -308,20 +278,15 @@ T sph_bessel_i_jac(long n, T z) {
308278
}
309279
}
310280

311-
return sph_bessel_i(n - 1, z) - static_cast<T>(n + 1) * sph_bessel_i(n, z) / z;
281+
return sph_bessel_i(n - 1, z) - (static_cast<T>(n) + 1) * sph_bessel_i(n, z) / z;
312282
}
313283

314284
template <typename T>
315-
T sph_bessel_k(long n, T z) {
285+
T sph_bessel_k(uint64_t n, T z) {
316286
if (std::isnan(z)) {
317287
return z;
318288
}
319289

320-
if (n < 0) {
321-
set_error("spherical_kn", SF_ERROR_DOMAIN, nullptr);
322-
return std::numeric_limits<T>::quiet_NaN();
323-
}
324-
325290
if (z == 0) {
326291
return std::numeric_limits<T>::infinity();
327292
}
@@ -339,16 +304,11 @@ T sph_bessel_k(long n, T z) {
339304
}
340305

341306
template <typename T>
342-
std::complex<T> sph_bessel_k(long n, std::complex<T> z) {
307+
std::complex<T> sph_bessel_k(uint64_t n, std::complex<T> z) {
343308
if (std::isnan(std::real(z)) || std::isnan(std::imag(z))) {
344309
return z;
345310
}
346311

347-
if (n < 0) {
348-
set_error("spherical_kn", SF_ERROR_DOMAIN, nullptr);
349-
return std::numeric_limits<T>::quiet_NaN();
350-
}
351-
352312
if (std::abs(z) == 0) {
353313
return std::numeric_limits<T>::quiet_NaN();
354314
}
@@ -370,12 +330,12 @@ std::complex<T> sph_bessel_k(long n, std::complex<T> z) {
370330
}
371331

372332
template <typename T>
373-
T sph_bessel_k_jac(long n, T x) {
333+
T sph_bessel_k_jac(uint64_t n, T x) {
374334
if (n == 0) {
375335
return -sph_bessel_k(1, x);
376336
}
377337

378-
return -sph_bessel_k(n - 1, x) - static_cast<T>(n + 1) * sph_bessel_k(n, x) / x;
338+
return -sph_bessel_k(n - 1, x) - (static_cast<T>(n) + 1) * sph_bessel_k(n, x) / x;
379339
}
380340

381341
} // namespace xsf

0 commit comments

Comments
 (0)