UPDATE: Adds a bunch of mathematical functions (#496)

This commit is contained in:
Nicolás Hatcher Andrés
2025-11-01 19:32:49 +01:00
committed by GitHub
parent c8ae835bbe
commit b2d848ae2a
5 changed files with 429 additions and 1 deletions

View File

@@ -381,6 +381,317 @@ impl Model {
}
}
// (number, divisor)
pub(crate) fn fn_mod(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.len() != 2 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let divisor = match self.get_number(&args[1], cell) {
Ok(f) => f,
Err(s) => return s,
};
if divisor == 0.0 {
return CalcResult::Error {
error: Error::DIV,
origin: cell,
message: "Divide by 0".to_string(),
};
}
let result = value - divisor * (value / divisor).floor();
CalcResult::Number(result)
}
pub(crate) fn fn_quotient(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.len() != 2 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let divisor = match self.get_number(&args[1], cell) {
Ok(f) => f,
Err(s) => return s,
};
if divisor == 0.0 {
return CalcResult::Error {
error: Error::DIV,
origin: cell,
message: "Divide by 0".to_string(),
};
}
let result = value / divisor;
CalcResult::Number(result.signum() * result.abs().floor())
}
pub(crate) fn fn_floor(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.len() != 2 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let significance = match self.get_number(&args[1], cell) {
Ok(f) => f,
Err(s) => return s,
};
if significance == 0.0 {
if value == 0.0 {
return CalcResult::Number(0.0);
}
return CalcResult::Error {
error: Error::DIV,
origin: cell,
message: "Divide by 0".to_string(),
};
}
if significance < 0.0 && value > 0.0 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Significance must be positive when value is positive".to_string(),
};
}
let result = f64::floor(value / significance) * significance;
CalcResult::Number(result)
}
pub(crate) fn fn_ceiling(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.len() != 2 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let significance = match self.get_number(&args[1], cell) {
Ok(f) => f,
Err(s) => return s,
};
if significance == 0.0 {
// This behaviour is different from FLOOR where division by zero returns an error
return CalcResult::Number(0.0);
}
if significance < 0.0 && value > 0.0 {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "Significance must be positive when value is positive".to_string(),
};
}
let result = f64::ceil(value / significance) * significance;
CalcResult::Number(result)
}
pub(crate) fn fn_ceiling_math(
&mut self,
args: &[Node],
cell: CellReferenceIndex,
) -> CalcResult {
let arg_count = args.len();
if arg_count > 3 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let significance = if arg_count > 1 {
match self.get_number(&args[1], cell) {
Ok(f) => f.abs(),
Err(s) => return s,
}
} else {
1.0
};
let mode = if arg_count > 2 {
match self.get_number(&args[2], cell) {
Ok(f) => f,
Err(s) => return s,
}
} else {
0.0
};
if significance == 0.0 {
return CalcResult::Number(0.0);
}
if value < 0.0 && mode != 0.0 {
let result = f64::floor(value / significance) * significance;
CalcResult::Number(result)
} else {
let result = f64::ceil(value / significance) * significance;
CalcResult::Number(result)
}
}
pub(crate) fn fn_ceiling_precise(
&mut self,
args: &[Node],
cell: CellReferenceIndex,
) -> CalcResult {
let arg_count = args.len();
if arg_count > 2 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let significance = if arg_count > 1 {
match self.get_number(&args[1], cell) {
Ok(f) => f.abs(),
Err(s) => return s,
}
} else {
1.0
};
if significance == 0.0 {
return CalcResult::Number(0.0);
}
let result = f64::ceil(value / significance) * significance;
CalcResult::Number(result)
}
pub(crate) fn fn_iso_ceiling(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
// ISO.CEILING is equivalent to CEILING.PRECISE
self.fn_ceiling_precise(args, cell)
}
pub(crate) fn fn_floor_math(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
let arg_count = args.len();
if arg_count > 3 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let significance = if arg_count > 1 {
match self.get_number(&args[1], cell) {
Ok(f) => f,
Err(s) => return s,
}
} else {
1.0
};
let mode = if arg_count > 2 {
match self.get_number(&args[2], cell) {
Ok(f) => f,
Err(s) => return s,
}
} else {
0.0
};
if significance == 0.0 {
return CalcResult::Number(0.0);
}
let significance = significance.abs();
if value < 0.0 && mode != 0.0 {
let result = f64::ceil(value / significance) * significance;
CalcResult::Number(result)
} else {
let result = f64::floor(value / significance) * significance;
CalcResult::Number(result)
}
}
pub(crate) fn fn_floor_precise(
&mut self,
args: &[Node],
cell: CellReferenceIndex,
) -> CalcResult {
let arg_count = args.len();
if arg_count > 2 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let significance = if arg_count > 1 {
match self.get_number(&args[1], cell) {
Ok(f) => f.abs(),
Err(s) => return s,
}
} else {
1.0
};
if significance == 0.0 {
return CalcResult::Number(0.0);
}
let result = f64::floor(value / significance) * significance;
CalcResult::Number(result)
}
pub(crate) fn fn_mround(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
// MROUND(number, multiple)
if args.len() != 2 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let multiple = match self.get_number(&args[1], cell) {
Ok(f) => f,
Err(s) => return s,
};
if multiple == 0.0 {
return CalcResult::Number(0.0);
}
if (value > 0.0 && multiple < 0.0) || (value < 0.0 && multiple > 0.0) {
return CalcResult::Error {
error: Error::NUM,
origin: cell,
message: "number and multiple must have the same sign".to_string(),
};
}
let result = (value / multiple).round() * multiple;
CalcResult::Number(result)
}
pub(crate) fn fn_trunc(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.len() > 2 {
return CalcResult::new_args_number_error(cell);
}
let value = match self.get_number(&args[0], cell) {
Ok(f) => f,
Err(s) => return s,
};
let num_digits = if args.len() == 2 {
match self.get_number(&args[1], cell) {
Ok(f) => {
if f > 0.0 {
f.floor()
} else {
f.ceil()
}
}
Err(s) => return s,
}
} else {
0.0
};
if !(-15.0..=15.0).contains(&num_digits) {
return CalcResult::Number(value);
}
CalcResult::Number(if value >= 0.0 {
f64::floor(value * 10f64.powf(num_digits)) / 10f64.powf(num_digits)
} else {
f64::ceil(value * 10f64.powf(num_digits)) / 10f64.powf(num_digits)
})
}
single_number_fn!(fn_log10, |f| if f <= 0.0 {
Err(Error::NUM)
} else {
@@ -487,6 +798,14 @@ impl Model {
single_number_fn!(fn_sign, |f| Ok(f64::signum(f)));
single_number_fn!(fn_degrees, |f| Ok(f * (180.0 / PI)));
single_number_fn!(fn_radians, |f| Ok(f * (PI / 180.0)));
single_number_fn!(fn_odd, |f| {
let sign = f64::signum(f);
Ok(sign * (f64::ceil((f64::abs(f) - 1.0) / 2.0) * 2.0 + 1.0))
});
single_number_fn!(fn_even, |f| Ok(f64::signum(f)
* f64::ceil(f64::abs(f) / 2.0)
* 2.0));
single_number_fn!(fn_int, |f| Ok(f64::floor(f)));
pub(crate) fn fn_pi(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if !args.is_empty() {