Skip to content

Add ln_1p and exp_m1 #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
20 changes: 20 additions & 0 deletions src/complex_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,22 @@ pub trait ComplexFloat: Num + NumCast + Copy + Neg<Output = Self> + private::Sea
///
/// Formula: `a+bi -> a-bi`
fn conj(self) -> Self;

/// Returns ln(1+n) (natural logarithm) more accurately
/// than if the operations were performed separately
///
/// Formula: ln(1+z)
///
/// where z = a+bi
fn ln_1p(self) -> Self;

/// Returns e^(self) - 1 in a way that is accurate
/// even if the number is close to zero
///
/// Formaula: e^(z) - 1
///
/// where z = a+bi
fn exp_m1(self) -> Self;
}

macro_rules! forward {
Expand Down Expand Up @@ -235,6 +251,8 @@ where
Float::acosh(self) -> Self;
Float::atanh(self) -> Self;
Float::abs(self) -> Self;
Float::ln_1p(self) -> Self;
Float::exp_m1(self) -> Self;
}
}

Expand Down Expand Up @@ -306,6 +324,8 @@ impl<T: Float + FloatConst> ComplexFloat for Complex<T> {
Complex::asinh(self) -> Self;
Complex::acosh(self) -> Self;
Complex::atanh(self) -> Self;
Complex::ln_1p(self) -> Self;
Complex::exp_m1(self) -> Self;
}

forward_ref! {
Expand Down
58 changes: 58 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,48 @@ impl<T: Float> Complex<T> {
pub fn fdiv(self, other: Complex<T>) -> Complex<T> {
self * other.finv()
}

/// The ln_1p() function computes the natural logarithm
/// of (1 + z), which is particularly useful for
/// small values of `z` to avoid loss of precision
///
/// # Examples
///
/// ```rust
/// use num_complex::Complex64;
/// use num_complex::ComplexFloat;
///
/// let a = Complex64::new(2.0e-16, 3.0e-16);
///
/// let approx_val = a.ln_1p();
/// let expected_val = Complex64::new(2.2204e-16, 2.999e-16);
/// assert!((approx_val - expected_val).norm() < 1e-18);
/// ```
#[inline]
pub fn ln_1p(self) -> Self {
(Self::one() + self).ln()
}

/// The exp_m1() function computes the exponential
/// of z minus one (`e^(z) - 1`), which is particularly
/// useful for small values of `z` to avoid loss of precision
///
/// # Examples
///
/// ```rust
/// use num_complex::Complex64;
/// use num_complex::ComplexFloat;
///
/// let a = Complex64::new(2.0e-16, 3.0e-16);
///
/// let approx_val = a.exp_m1();
/// let expected_val = Complex64::new(2.2204e-16, 3.0e-16);
/// assert!((approx_val - expected_val).norm() < 1e-18);
/// ```
#[inline]
pub fn exp_m1(self) -> Self {
self.exp() - Self::one()
}
}

#[cfg(any(feature = "std", feature = "libm"))]
Expand Down Expand Up @@ -2432,6 +2474,22 @@ pub(crate) mod test {
));
}
}

#[test]
fn test_ln_1p() {
for &c in all_consts.iter() {
// ln_1p(z) = ln(1+z)
assert!(close(c.ln_1p(), (1.0 + c).ln()));
}
}

#[test]
fn test_exp_m1() {
for &c in all_consts.iter() {
// exp_m1(z) = exp(z) - 1
assert!(close(c.exp_m1(), (c).exp() - 1.0));
}
}
}

// Test both a + b and a += b
Expand Down