use super::funejson::Value; use super::math_round_float::round_float; const POW10: [f64; 13] = [ 1.0, 10.0, 100.0, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, ]; /// A float as decimal text, never in exponent form and never "-0". /// /// Display, str() and String() disagree about floats, so the number is /// rounded once, turned into a whole count of 10^-decimals units, and the /// digits of that integer are laid out by hand. /// /// # Panics /// Panics if `value` is not finite, `decimals` is outside 0..=12, or the /// value times 10^decimals is 10^15 or more. pub fn format_decimal(value: f64, decimals: i64, trim_zeros: bool, group_separator: &str) -> String { let rounded = round_float(value, decimals); let places = decimals as usize; let scaled = rounded.abs() * POW10[places]; if scaled >= 1e15 { panic!("value is too large to format at {} decimal places, received {}", decimals, value); } let units = round_float(scaled, 0) as i64; let mut digits = units.to_string(); while digits.len() < places + 1 { digits.insert(0, '0'); } let split = digits.len() - places; let whole = &digits[..split]; let mut fraction = &digits[split..]; if trim_zeros { fraction = fraction.trim_end_matches('0'); } let mut grouped = String::new(); for (i, ch) in whole.chars().enumerate() { if i > 0 && (whole.len() - i) % 3 == 0 { grouped.push_str(group_separator); } grouped.push(ch); } let body = if fraction.is_empty() { grouped } else { format!("{}.{}", grouped, fraction) }; if units > 0 && rounded < 0.0 { format!("-{}", body) } else { body } } pub fn fune_vector(args: &[Value]) -> Value { Value::str(&format_decimal(args[0].as_f64(), args[1].as_i64(), args[2].as_bool(), args[3].as_str())) }