pub fn parse_f64(s: &str) -> Option<f64> {
s.parse::<f64>().ok()
}
pub fn parse_f64_opt(s: &Option<String>) -> Option<f64> {
s.as_deref().and_then(|x| x.parse::<f64>().ok())
}
pub fn round_f64(value: f64, precision: i32) -> f64 {
let factor = 10f64.powi(precision);
(value * factor).round() / factor
}
pub fn round_f64_opt(value: Option<f64>, precision: i32) -> Option<f64> {
value.map(|v| round_f64(v, precision))
}
pub fn decimals_from_string(s: &str, delimiter: char) -> u32 {
s.split(delimiter)
.nth(1)
.map(|frac| frac.trim_end_matches('0').len() as u32)
.unwrap_or(0)
}
pub fn decimals_from_float(step: f64) -> u32 {
if step <= 0.0 {
return 0;
}
let s = format!("{:.12}", step);
if let Some(frac) = s.split('.').nth(1) {
frac.trim_end_matches('0').len() as u32
} else {
0
}
}
pub fn step_from_precision(precision: u32) -> f64 {
10f64.powi(-(precision as i32))
}