FIX: Format numbers a tad better

I still think there is some way to go, but this is closer to Excel
This commit is contained in:
Nicolás Hatcher
2025-11-19 21:50:16 +01:00
committed by Nicolás Hatcher Andrés
parent acb90fbb9d
commit dc49afa2c3
5 changed files with 55 additions and 17 deletions

View File

@@ -112,29 +112,36 @@ pub fn to_precision(value: f64, precision: usize) -> f64 {
/// ```
/// This intends to be equivalent to the js: `${parseFloat(value.toPrecision(precision)})`
/// See ([ecma](https://tc39.es/ecma262/#sec-number.prototype.toprecision)).
/// FIXME: There has to be a better algorithm :/
pub fn to_excel_precision_str(value: f64) -> String {
to_precision_str(value, 15)
}
pub fn to_excel_precision(value: f64, precision: usize) -> f64 {
if !value.is_finite() {
return value;
}
let s = format!("{:.*e}", precision.saturating_sub(1), value);
s.parse::<f64>().unwrap_or(value)
}
pub fn to_precision_str(value: f64, precision: usize) -> String {
if value.is_infinite() {
return "inf".to_string();
if !value.is_finite() {
if value.is_infinite() {
return "inf".to_string();
} else {
return "NaN".to_string();
}
}
if value.is_nan() {
return "NaN".to_string();
}
let exponent = value.abs().log10().floor();
let base = value / 10.0_f64.powf(exponent);
let base = format!("{0:.1$}", base, precision - 1);
let value = format!("{base}e{exponent}").parse::<f64>().unwrap_or({
// TODO: do this in a way that does not require a possible error
0.0
});
let s = format!("{:.*e}", precision.saturating_sub(1), value);
let parsed = s.parse::<f64>().unwrap_or(value);
// I would love to use the std library. There is not a speed concern here
// problem is it doesn't do the right thing
// Also ryu is my favorite _modern_ algorithm
let mut buffer = ryu::Buffer::new();
let text = buffer.format(value);
let text = buffer.format(parsed);
// The above algorithm converts 2 to 2.0 regrettably
if let Some(stripped) = text.strip_suffix(".0") {
return stripped.to_string();