diff --git a/Cargo.toml b/Cargo.toml index 149072c..b017e3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ categories = ["algorithms", "data-structures", "science", "no-std"] license = "MIT OR Apache-2.0" name = "num-complex" repository = "https://github.com/rust-num/num-complex" -version = "0.4.3" +version = "0.4.4" readme = "README.md" exclude = ["/bors.toml", "/ci/*", "/.github/*"] edition = "2018" diff --git a/RELEASES.md b/RELEASES.md index d6244d8..1276628 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,9 +1,19 @@ +# Release 0.4.4 (2023-08-13) + +- [Fixes NaN value for `powc` of zero][116] + +**Contributors**: @cuviper, @domna + +[116]: https://github.com/rust-num/num-complex/pull/116 + # Release 0.4.3 (2023-01-19) - [`Complex` now optionally supports `bytecheck` 0.6 and `rkyv` 0.7][110]. **Contributors**: @cuviper, @zyansheep +[110]: https://github.com/rust-num/num-complex/pull/110 + # Release 0.4.2 (2022-06-17) - [The new `ComplexFloat` trait][95] provides a generic abstraction between diff --git a/ci/test_full.sh b/ci/test_full.sh index 6cafcc5..60e9740 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -38,6 +38,14 @@ cargo generate-lockfile # libm 0.2.6 started using {float}::EPSILON check_version 1.43 || cargo update -p libm --precise 0.2.5 +# Some crates moved to Rust 1.56 / 2021 +check_version 1.56 || ( + cargo update -p quote --precise 1.0.30 + cargo update -p proc-macro2 --precise 1.0.65 + cargo update -p rkyv --precise 0.7.40 + cargo update -p bytecheck --precise 0.6.9 +) + set -x # test the default diff --git a/src/lib.rs b/src/lib.rs index 1860d28..cb847b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -342,6 +342,9 @@ impl Complex { /// Raises `self` to a floating point power. #[inline] pub fn powf(self, exp: T) -> Self { + if exp.is_zero() { + return Self::one(); + } // formula: x^y = (ρ e^(i θ))^y = ρ^y e^(i θ y) // = from_polar(ρ^y, θ y) let (r, theta) = self.to_polar(); @@ -361,22 +364,11 @@ impl Complex { /// Raises `self` to a complex power. #[inline] pub fn powc(self, exp: Self) -> Self { - // formula: x^y = (a + i b)^(c + i d) - // = (ρ e^(i θ))^c (ρ e^(i θ))^(i d) - // where ρ=|x| and θ=arg(x) - // = ρ^c e^(−d θ) e^(i c θ) ρ^(i d) - // = p^c e^(−d θ) (cos(c θ) - // + i sin(c θ)) (cos(d ln(ρ)) + i sin(d ln(ρ))) - // = p^c e^(−d θ) ( - // cos(c θ) cos(d ln(ρ)) − sin(c θ) sin(d ln(ρ)) - // + i(cos(c θ) sin(d ln(ρ)) + sin(c θ) cos(d ln(ρ)))) - // = p^c e^(−d θ) (cos(c θ + d ln(ρ)) + i sin(c θ + d ln(ρ))) - // = from_polar(p^c e^(−d θ), c θ + d ln(ρ)) - let (r, theta) = self.to_polar(); - Self::from_polar( - r.powf(exp.re) * (-exp.im * theta).exp(), - exp.re * theta + exp.im * r.ln(), - ) + if exp.is_zero() { + return Self::one(); + } + // formula: x^y = exp(y * ln(x)) + (exp * self.ln()).exp() } /// Raises a floating point number to the complex power `self`. @@ -1715,6 +1707,9 @@ pub(crate) mod test { #[cfg(any(feature = "std", feature = "libm"))] pub(crate) mod float { + + use core::f64::INFINITY; + use super::*; use num_traits::{Float, Pow}; @@ -1908,6 +1903,19 @@ pub(crate) mod test { Complex::new(1.65826, -0.33502), 1e-5 )); + let z = Complex::new(0.0, 0.0); + assert!(close(z.powc(b), z)); + assert!(z.powc(Complex64::new(0., INFINITY)).is_nan()); + assert!(z.powc(Complex64::new(10., INFINITY)).is_nan()); + assert!(z.powc(Complex64::new(INFINITY, INFINITY)).is_nan()); + assert!(close(z.powc(Complex64::new(INFINITY, 0.)), z)); + assert!(z.powc(Complex64::new(-1., 0.)).re.is_infinite()); + assert!(z.powc(Complex64::new(-1., 0.)).im.is_nan()); + + for c in all_consts.iter() { + assert_eq!(c.powc(_0_0i), _1_0i); + } + assert_eq!(_nan_nani.powc(_0_0i), _1_0i); } #[test] @@ -1917,6 +1925,11 @@ pub(crate) mod test { assert!(close_to_tol(c.powf(3.5), expected, 1e-5)); assert!(close_to_tol(Pow::pow(c, 3.5_f64), expected, 1e-5)); assert!(close_to_tol(Pow::pow(c, 3.5_f32), expected, 1e-5)); + + for c in all_consts.iter() { + assert_eq!(c.powf(0.0), _1_0i); + } + assert_eq!(_nan_nani.powf(0.0), _1_0i); } #[test]