Skip to content
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
Cargo.lock
cpython
3 changes: 1 addition & 2 deletions src/cmath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ pub(crate) mod tests {
}
Err(e) => {
// CPython raised an exception - check we got an error too
if rs_result.is_ok() {
let rs = rs_result.unwrap();
if let Ok(rs) = rs_result {
// Some special cases may return values for domain errors in Python
// Check if it's a domain error
if e.is_instance_of::<pyo3::exceptions::PyValueError>(py) {
Expand Down
25 changes: 13 additions & 12 deletions src/cmath/exponential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{
CM_LARGE_DOUBLE, CM_LOG_LARGE_DOUBLE, INF, M_LN2, N, P, P12, P14, P34, U, c, special_type,
special_value,
};
use crate::{Error, Result, m};
use crate::{Error, Result, m, mul_add};
use num_complex::Complex64;

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

#[test]
fn edgetest_sqrt() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_sqrt(re, im);
}
}
}

#[test]
fn edgetest_exp() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_exp(re, im);
}
}
}

#[test]
fn edgetest_log_n() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_log_n(re, im);
}
}
}

#[test]
fn edgetest_log10() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_log10(re, im);
}
}
Expand All @@ -373,8 +374,8 @@ mod tests {
}
}
// Additional edge cases with imaginary parts
for &z_re in &EDGE_VALUES {
for &z_im in &EDGE_VALUES {
for &z_re in EDGE_VALUES {
for &z_im in EDGE_VALUES {
test_log(z_re, z_im, 2.0, 0.0);
test_log(z_re, z_im, 0.5, 0.0);
}
Expand Down
52 changes: 39 additions & 13 deletions src/cmath/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,41 @@ pub fn phase(z: Complex64) -> Result<f64> {
}
}

#[inline]
fn c_abs_raw(z: Complex64) -> f64 {
if !z.re.is_finite() || !z.im.is_finite() {
// C99 rules: if either part is infinite, return infinity,
// even if the other part is NaN.
if z.re.is_infinite() {
return m::fabs(z.re);
}
if z.im.is_infinite() {
return m::fabs(z.im);
}
return f64::NAN;
}
m::hypot(z.re, z.im)
}

#[inline]
fn c_abs_checked(z: Complex64) -> Result<f64> {
if !z.re.is_finite() || !z.im.is_finite() {
return Ok(c_abs_raw(z));
}
crate::err::set_errno(0);
let r = m::hypot(z.re, z.im);
if r.is_infinite() {
Err(Error::ERANGE)
} else {
Ok(r)
}
}

/// Convert z to polar coordinates (r, phi).
#[inline]
pub fn polar(z: Complex64) -> Result<(f64, f64)> {
let phi = m::atan2(z.im, z.re);
let r = m::hypot(z.re, z.im);
if r.is_infinite() && z.re.is_finite() && z.im.is_finite() {
return Err(Error::ERANGE);
}
let r = c_abs_checked(z)?;
Ok((r, phi))
}

Expand Down Expand Up @@ -94,7 +121,7 @@ pub fn isinf(z: Complex64) -> bool {
/// Complex absolute value (magnitude).
#[inline]
pub fn abs(z: Complex64) -> f64 {
m::hypot(z.re, z.im)
c_abs_raw(z)
}

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

#[test]
fn edgetest_phase() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_phase_impl(re, im);
}
}
Expand Down Expand Up @@ -250,8 +276,8 @@ mod tests {

#[test]
fn edgetest_polar() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_polar_impl(re, im);
}
}
Expand Down Expand Up @@ -297,8 +323,8 @@ mod tests {

#[test]
fn edgetest_rect() {
for &r in &EDGE_VALUES {
for &phi in &EDGE_VALUES {
for &r in EDGE_VALUES {
for &phi in EDGE_VALUES {
test_rect_impl(r, phi);
}
}
Expand Down
48 changes: 24 additions & 24 deletions src/cmath/trigonometric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,107 +421,107 @@ mod tests {

#[test]
fn edgetest_sin() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_sin(re, im);
}
}
}

#[test]
fn edgetest_cos() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_cos(re, im);
}
}
}

#[test]
fn edgetest_tan() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_tan(re, im);
}
}
}

#[test]
fn edgetest_sinh() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_sinh(re, im);
}
}
}

#[test]
fn edgetest_cosh() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_cosh(re, im);
}
}
}

#[test]
fn edgetest_tanh() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_tanh(re, im);
}
}
}

#[test]
fn edgetest_asin() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_asin(re, im);
}
}
}

#[test]
fn edgetest_acos() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_acos(re, im);
}
}
}

#[test]
fn edgetest_atan() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_atan(re, im);
}
}
}

#[test]
fn edgetest_asinh() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_asinh(re, im);
}
}
}

#[test]
fn edgetest_acosh() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_acosh(re, im);
}
}
}

#[test]
fn edgetest_atanh() {
for &re in &EDGE_VALUES {
for &im in &EDGE_VALUES {
for &re in EDGE_VALUES {
for &im in EDGE_VALUES {
test_atanh(re, im);
}
}
Expand Down
38 changes: 20 additions & 18 deletions src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,19 @@ pub fn hypot(coords: &[f64]) -> f64 {

let mut max = 0.0_f64;
let mut found_nan = false;
let abs_coords: Vec<f64> = coords
.iter()
.map(|&x| {
let ax = x.abs();
found_nan |= ax.is_nan();
if ax > max {
max = ax;
}
ax
})
.collect();
let mut abs_coords = Vec::with_capacity(n);

for &x in coords {
let ax = x.abs();
// Use is_nan() check separately to prevent compiler from
// reordering NaN detection relative to max comparison
if ax.is_nan() {
found_nan = true;
} else if ax > max {
max = ax;
}
abs_coords.push(ax);
}

aggregate::vector_norm(&abs_coords, max, found_nan)
}
Expand Down Expand Up @@ -206,24 +208,24 @@ mod tests {

#[test]
fn edgetest_degrees() {
for &x in &crate::test::EDGE_VALUES {
for &x in crate::test::EDGE_VALUES {
test_degrees(x);
}
}

#[test]
fn edgetest_radians() {
for &x in &crate::test::EDGE_VALUES {
for &x in crate::test::EDGE_VALUES {
test_radians(x);
}
}

// Constants test
#[test]
fn test_constants() {
assert!((PI - 3.141592653589793).abs() < 1e-15);
assert!((E - 2.718281828459045).abs() < 1e-15);
assert!((TAU - 6.283185307179586).abs() < 1e-15);
assert_eq!(PI, std::f64::consts::PI);
assert_eq!(E, std::f64::consts::E);
assert_eq!(TAU, std::f64::consts::TAU);
assert!(INF.is_infinite() && INF > 0.0);
assert!(NAN.is_nan());
}
Expand Down Expand Up @@ -262,8 +264,8 @@ mod tests {

#[test]
fn edgetest_hypot() {
for &x in &crate::test::EDGE_VALUES {
for &y in &crate::test::EDGE_VALUES {
for &x in crate::test::EDGE_VALUES {
for &y in crate::test::EDGE_VALUES {
test_hypot(&[x, y]);
}
}
Expand Down
Loading