Functional Weave
Code in Rust

text.format-decimal@1.0.0

impl/rust.rs

1,869 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_round_float::round_float;  ← from math.round-float ^1.0.0 · built alongside by fune

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()))
}