add calc.exp, calc.ln (#1299)

Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
Jett Chen 2023-06-07 20:44:07 +08:00 committed by GitHub
parent 0dc1776202
commit c87f802cf6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 5 deletions

View File

@ -13,6 +13,7 @@ pub fn module() -> Module {
let mut scope = Scope::new();
scope.define("abs", abs_func());
scope.define("pow", pow_func());
scope.define("exp", exp_func());
scope.define("sqrt", sqrt_func());
scope.define("sin", sin_func());
scope.define("cos", cos_func());
@ -25,6 +26,7 @@ pub fn module() -> Module {
scope.define("cosh", cosh_func());
scope.define("tanh", tanh_func());
scope.define("log", log_func());
scope.define("ln", ln_func());
scope.define("fact", fact_func());
scope.define("perm", perm_func());
scope.define("binom", binom_func());
@ -96,7 +98,7 @@ cast! {
pub fn pow(
/// The base of the power.
base: Num,
/// The exponent of the power. Must be non-negative.
/// The exponent of the power.
exponent: Spanned<Num>,
/// The callsite span.
span: Span,
@ -120,8 +122,15 @@ pub fn pow(
.map(Num::Int)
.ok_or("the result is too large")
.at(span)?,
(a, Num::Int(b)) => Num::Float(a.float().powi(b as i32)),
(a, b) => Num::Float(a.float().powf(b.float())),
(a, b) => Num::Float(if a.float() == std::f64::consts::E {
b.float().exp()
} else if a.float() == 2.0 {
b.float().exp2()
} else if let Num::Int(b) = b {
a.float().powi(b as i32)
} else {
a.float().powf(b.float())
}),
};
if result.float().is_nan() {
@ -131,6 +140,40 @@ pub fn pow(
Ok(result)
}
/// Raise a value to some exponent of e.
///
/// ## Example { #example }
/// ```example
/// #calc.exp(3)
/// ```
///
/// Display: Exponential
/// Category: calculate
#[func]
pub fn exp(
/// The exponent of the power.
exponent: Spanned<Num>,
/// The callsite span.
span: Span,
) -> SourceResult<f64> {
match exponent.v {
Num::Int(i) if i32::try_from(i).is_err() => {
bail!(exponent.span, "exponent is too large")
}
Num::Float(f) if !f.is_normal() && f != 0.0 => {
bail!(exponent.span, "exponent may not be infinite, subnormal, or NaN")
}
_ => {}
};
let result = exponent.v.float().exp();
if result.is_nan() {
bail!(span, "the result is not a real number")
}
Ok(result)
}
/// Calculate the square root of a number.
///
/// ## Example { #example }
@ -416,7 +459,9 @@ pub fn log(
bail!(base.span, "base may not be zero, NaN, infinite, or subnormal")
}
let result = if base.v == 2.0 {
let result = if base.v == std::f64::consts::E {
number.ln()
} else if base.v == 2.0 {
number.log2()
} else if base.v == 10.0 {
number.log10()
@ -431,6 +476,35 @@ pub fn log(
Ok(result)
}
/// Calculate the natural logarithm of a number.
///
/// ## Example { #example }
/// ```example
/// #calc.ln(100)
/// ```
///
/// Display: Natural Logarithm
/// Category: calculate
#[func]
pub fn ln(
/// The number whose logarithm to calculate. Must be strictly positive.
value: Spanned<Num>,
/// The callsite span.
span: Span,
) -> SourceResult<f64> {
let number = value.v.float();
if number <= 0.0 {
bail!(value.span, "value must be strictly positive")
}
let result = number.ln();
if result.is_infinite() {
bail!(span, "result close to -inf")
}
Ok(result)
}
/// Calculate the factorial of a number.
///
/// ## Example { #example }

View File

@ -94,9 +94,11 @@
#test(calc.min("hi"), "hi")
---
// Test the `calc` function.
// Test the `pow`, `log`, `exp`, and `ln` functions.
#test(calc.pow(10, 0), 1)
#test(calc.pow(2, 4), 16)
#test(calc.exp(2), calc.pow(calc.e, 2))
#test(calc.ln(10), calc.log(10, base: calc.e))
---
// Error: 10-16 zero to the power of zero is undefined