Skip to content

Commit bcf75ff

Browse files
authored
more edge values (#14)
1 parent 97d2f52 commit bcf75ff

File tree

11 files changed

+244
-135
lines changed

11 files changed

+244
-135
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
22
Cargo.lock
3+
cpython

src/cmath.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,7 @@ pub(crate) mod tests {
178178
}
179179
Err(e) => {
180180
// CPython raised an exception - check we got an error too
181-
if rs_result.is_ok() {
182-
let rs = rs_result.unwrap();
181+
if let Ok(rs) = rs_result {
183182
// Some special cases may return values for domain errors in Python
184183
// Check if it's a domain error
185184
if e.is_instance_of::<pyo3::exceptions::PyValueError>(py) {

src/cmath/exponential.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::{
44
CM_LARGE_DOUBLE, CM_LOG_LARGE_DOUBLE, INF, M_LN2, N, P, P12, P14, P34, U, c, special_type,
55
special_value,
66
};
7-
use crate::{Error, Result, m};
7+
use crate::{Error, Result, m, mul_add};
88
use num_complex::Complex64;
99

1010
// Local constants
@@ -160,7 +160,8 @@ pub(crate) fn ln(z: Complex64) -> Result<Complex64> {
160160
if (0.71..=1.73).contains(&h) {
161161
let am = if ax > ay { ax } else { ay }; // max(ax, ay)
162162
let an = if ax > ay { ay } else { ax }; // min(ax, ay)
163-
m::log1p((am - 1.0) * (am + 1.0) + an * an) / 2.0
163+
let log1p_arg = mul_add(am - 1.0, am + 1.0, an * an);
164+
m::log1p(log1p_arg) / 2.0
164165
} else {
165166
m::log(h)
166167
}
@@ -328,35 +329,35 @@ mod tests {
328329

329330
#[test]
330331
fn edgetest_sqrt() {
331-
for &re in &EDGE_VALUES {
332-
for &im in &EDGE_VALUES {
332+
for &re in EDGE_VALUES {
333+
for &im in EDGE_VALUES {
333334
test_sqrt(re, im);
334335
}
335336
}
336337
}
337338

338339
#[test]
339340
fn edgetest_exp() {
340-
for &re in &EDGE_VALUES {
341-
for &im in &EDGE_VALUES {
341+
for &re in EDGE_VALUES {
342+
for &im in EDGE_VALUES {
342343
test_exp(re, im);
343344
}
344345
}
345346
}
346347

347348
#[test]
348349
fn edgetest_log_n() {
349-
for &re in &EDGE_VALUES {
350-
for &im in &EDGE_VALUES {
350+
for &re in EDGE_VALUES {
351+
for &im in EDGE_VALUES {
351352
test_log_n(re, im);
352353
}
353354
}
354355
}
355356

356357
#[test]
357358
fn edgetest_log10() {
358-
for &re in &EDGE_VALUES {
359-
for &im in &EDGE_VALUES {
359+
for &re in EDGE_VALUES {
360+
for &im in EDGE_VALUES {
360361
test_log10(re, im);
361362
}
362363
}
@@ -373,8 +374,8 @@ mod tests {
373374
}
374375
}
375376
// Additional edge cases with imaginary parts
376-
for &z_re in &EDGE_VALUES {
377-
for &z_im in &EDGE_VALUES {
377+
for &z_re in EDGE_VALUES {
378+
for &z_im in EDGE_VALUES {
378379
test_log(z_re, z_im, 2.0, 0.0);
379380
test_log(z_re, z_im, 0.5, 0.0);
380381
}

src/cmath/misc.rs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,41 @@ pub fn phase(z: Complex64) -> Result<f64> {
2828
}
2929
}
3030

31+
#[inline]
32+
fn c_abs_raw(z: Complex64) -> f64 {
33+
if !z.re.is_finite() || !z.im.is_finite() {
34+
// C99 rules: if either part is infinite, return infinity,
35+
// even if the other part is NaN.
36+
if z.re.is_infinite() {
37+
return m::fabs(z.re);
38+
}
39+
if z.im.is_infinite() {
40+
return m::fabs(z.im);
41+
}
42+
return f64::NAN;
43+
}
44+
m::hypot(z.re, z.im)
45+
}
46+
47+
#[inline]
48+
fn c_abs_checked(z: Complex64) -> Result<f64> {
49+
if !z.re.is_finite() || !z.im.is_finite() {
50+
return Ok(c_abs_raw(z));
51+
}
52+
crate::err::set_errno(0);
53+
let r = m::hypot(z.re, z.im);
54+
if r.is_infinite() {
55+
Err(Error::ERANGE)
56+
} else {
57+
Ok(r)
58+
}
59+
}
60+
3161
/// Convert z to polar coordinates (r, phi).
3262
#[inline]
3363
pub fn polar(z: Complex64) -> Result<(f64, f64)> {
3464
let phi = m::atan2(z.im, z.re);
35-
let r = m::hypot(z.re, z.im);
36-
if r.is_infinite() && z.re.is_finite() && z.im.is_finite() {
37-
return Err(Error::ERANGE);
38-
}
65+
let r = c_abs_checked(z)?;
3966
Ok((r, phi))
4067
}
4168

@@ -94,7 +121,7 @@ pub fn isinf(z: Complex64) -> bool {
94121
/// Complex absolute value (magnitude).
95122
#[inline]
96123
pub fn abs(z: Complex64) -> f64 {
97-
m::hypot(z.re, z.im)
124+
c_abs_raw(z)
98125
}
99126

100127
/// Determine whether two complex numbers are close in value.
@@ -169,8 +196,7 @@ mod tests {
169196
}
170197
Err(e) => {
171198
// Python raised an exception - check we got an error too
172-
if rs_result.is_ok() {
173-
let rs_val = rs_result.unwrap();
199+
if let Ok(rs_val) = rs_result {
174200
if e.is_instance_of::<pyo3::exceptions::PyValueError>(py) {
175201
panic!("phase({re}, {im}): py raised ValueError but rs={rs_val}");
176202
} else if e.is_instance_of::<pyo3::exceptions::PyOverflowError>(py) {
@@ -185,8 +211,8 @@ mod tests {
185211

186212
#[test]
187213
fn edgetest_phase() {
188-
for &re in &EDGE_VALUES {
189-
for &im in &EDGE_VALUES {
214+
for &re in EDGE_VALUES {
215+
for &im in EDGE_VALUES {
190216
test_phase_impl(re, im);
191217
}
192218
}
@@ -250,8 +276,8 @@ mod tests {
250276

251277
#[test]
252278
fn edgetest_polar() {
253-
for &re in &EDGE_VALUES {
254-
for &im in &EDGE_VALUES {
279+
for &re in EDGE_VALUES {
280+
for &im in EDGE_VALUES {
255281
test_polar_impl(re, im);
256282
}
257283
}
@@ -297,8 +323,8 @@ mod tests {
297323

298324
#[test]
299325
fn edgetest_rect() {
300-
for &r in &EDGE_VALUES {
301-
for &phi in &EDGE_VALUES {
326+
for &r in EDGE_VALUES {
327+
for &phi in EDGE_VALUES {
302328
test_rect_impl(r, phi);
303329
}
304330
}

src/cmath/trigonometric.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -421,107 +421,107 @@ mod tests {
421421

422422
#[test]
423423
fn edgetest_sin() {
424-
for &re in &EDGE_VALUES {
425-
for &im in &EDGE_VALUES {
424+
for &re in EDGE_VALUES {
425+
for &im in EDGE_VALUES {
426426
test_sin(re, im);
427427
}
428428
}
429429
}
430430

431431
#[test]
432432
fn edgetest_cos() {
433-
for &re in &EDGE_VALUES {
434-
for &im in &EDGE_VALUES {
433+
for &re in EDGE_VALUES {
434+
for &im in EDGE_VALUES {
435435
test_cos(re, im);
436436
}
437437
}
438438
}
439439

440440
#[test]
441441
fn edgetest_tan() {
442-
for &re in &EDGE_VALUES {
443-
for &im in &EDGE_VALUES {
442+
for &re in EDGE_VALUES {
443+
for &im in EDGE_VALUES {
444444
test_tan(re, im);
445445
}
446446
}
447447
}
448448

449449
#[test]
450450
fn edgetest_sinh() {
451-
for &re in &EDGE_VALUES {
452-
for &im in &EDGE_VALUES {
451+
for &re in EDGE_VALUES {
452+
for &im in EDGE_VALUES {
453453
test_sinh(re, im);
454454
}
455455
}
456456
}
457457

458458
#[test]
459459
fn edgetest_cosh() {
460-
for &re in &EDGE_VALUES {
461-
for &im in &EDGE_VALUES {
460+
for &re in EDGE_VALUES {
461+
for &im in EDGE_VALUES {
462462
test_cosh(re, im);
463463
}
464464
}
465465
}
466466

467467
#[test]
468468
fn edgetest_tanh() {
469-
for &re in &EDGE_VALUES {
470-
for &im in &EDGE_VALUES {
469+
for &re in EDGE_VALUES {
470+
for &im in EDGE_VALUES {
471471
test_tanh(re, im);
472472
}
473473
}
474474
}
475475

476476
#[test]
477477
fn edgetest_asin() {
478-
for &re in &EDGE_VALUES {
479-
for &im in &EDGE_VALUES {
478+
for &re in EDGE_VALUES {
479+
for &im in EDGE_VALUES {
480480
test_asin(re, im);
481481
}
482482
}
483483
}
484484

485485
#[test]
486486
fn edgetest_acos() {
487-
for &re in &EDGE_VALUES {
488-
for &im in &EDGE_VALUES {
487+
for &re in EDGE_VALUES {
488+
for &im in EDGE_VALUES {
489489
test_acos(re, im);
490490
}
491491
}
492492
}
493493

494494
#[test]
495495
fn edgetest_atan() {
496-
for &re in &EDGE_VALUES {
497-
for &im in &EDGE_VALUES {
496+
for &re in EDGE_VALUES {
497+
for &im in EDGE_VALUES {
498498
test_atan(re, im);
499499
}
500500
}
501501
}
502502

503503
#[test]
504504
fn edgetest_asinh() {
505-
for &re in &EDGE_VALUES {
506-
for &im in &EDGE_VALUES {
505+
for &re in EDGE_VALUES {
506+
for &im in EDGE_VALUES {
507507
test_asinh(re, im);
508508
}
509509
}
510510
}
511511

512512
#[test]
513513
fn edgetest_acosh() {
514-
for &re in &EDGE_VALUES {
515-
for &im in &EDGE_VALUES {
514+
for &re in EDGE_VALUES {
515+
for &im in EDGE_VALUES {
516516
test_acosh(re, im);
517517
}
518518
}
519519
}
520520

521521
#[test]
522522
fn edgetest_atanh() {
523-
for &re in &EDGE_VALUES {
524-
for &im in &EDGE_VALUES {
523+
for &re in EDGE_VALUES {
524+
for &im in EDGE_VALUES {
525525
test_atanh(re, im);
526526
}
527527
}

src/math.rs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,19 @@ pub fn hypot(coords: &[f64]) -> f64 {
114114

115115
let mut max = 0.0_f64;
116116
let mut found_nan = false;
117-
let abs_coords: Vec<f64> = coords
118-
.iter()
119-
.map(|&x| {
120-
let ax = x.abs();
121-
found_nan |= ax.is_nan();
122-
if ax > max {
123-
max = ax;
124-
}
125-
ax
126-
})
127-
.collect();
117+
let mut abs_coords = Vec::with_capacity(n);
118+
119+
for &x in coords {
120+
let ax = x.abs();
121+
// Use is_nan() check separately to prevent compiler from
122+
// reordering NaN detection relative to max comparison
123+
if ax.is_nan() {
124+
found_nan = true;
125+
} else if ax > max {
126+
max = ax;
127+
}
128+
abs_coords.push(ax);
129+
}
128130

129131
aggregate::vector_norm(&abs_coords, max, found_nan)
130132
}
@@ -206,24 +208,24 @@ mod tests {
206208

207209
#[test]
208210
fn edgetest_degrees() {
209-
for &x in &crate::test::EDGE_VALUES {
211+
for &x in crate::test::EDGE_VALUES {
210212
test_degrees(x);
211213
}
212214
}
213215

214216
#[test]
215217
fn edgetest_radians() {
216-
for &x in &crate::test::EDGE_VALUES {
218+
for &x in crate::test::EDGE_VALUES {
217219
test_radians(x);
218220
}
219221
}
220222

221223
// Constants test
222224
#[test]
223225
fn test_constants() {
224-
assert!((PI - 3.141592653589793).abs() < 1e-15);
225-
assert!((E - 2.718281828459045).abs() < 1e-15);
226-
assert!((TAU - 6.283185307179586).abs() < 1e-15);
226+
assert_eq!(PI, std::f64::consts::PI);
227+
assert_eq!(E, std::f64::consts::E);
228+
assert_eq!(TAU, std::f64::consts::TAU);
227229
assert!(INF.is_infinite() && INF > 0.0);
228230
assert!(NAN.is_nan());
229231
}
@@ -262,8 +264,8 @@ mod tests {
262264

263265
#[test]
264266
fn edgetest_hypot() {
265-
for &x in &crate::test::EDGE_VALUES {
266-
for &y in &crate::test::EDGE_VALUES {
267+
for &x in crate::test::EDGE_VALUES {
268+
for &y in crate::test::EDGE_VALUES {
267269
test_hypot(&[x, y]);
268270
}
269271
}

0 commit comments

Comments
 (0)