diff --git a/crates/numfmt/src/parse.rs b/crates/numfmt/src/parse.rs index e54f04c..6df22ad 100644 --- a/crates/numfmt/src/parse.rs +++ b/crates/numfmt/src/parse.rs @@ -513,6 +513,7 @@ fn analyze(raw: Raw) -> Section { let mut seen_exp = false; let mut seen_digit = false; let mut fraction_slash = false; + let scientific = toks.iter().skip_while(|t| !matches!(t, Tok::Digit(_))).any(|t| matches!(t, Tok::Exp { .. })); for i in 0..toks.len() { let Some(t) = toks.get(i).cloned() else { break }; let replace = match t { @@ -547,6 +548,10 @@ fn analyze(raw: Raw) -> Section { if seen_digit && next_digit && !seen_point && !seen_exp { sec.thousands = true; Some(Tok::Lit(String::new())) + } else if seen_digit && scientific { + // Observed: a scientific section never scales; any other comma after the + // first digit shows nothing (`#,e+#`, `#e+#,`, `#e+000,00`). + Some(Tok::Lit(String::new())) } else if seen_digit && !next_digit { sec.scale += 1; Some(Tok::Lit(String::new())) diff --git a/crates/numfmt/src/render.rs b/crates/numfmt/src/render.rs index d088231..64a4fca 100644 --- a/crates/numfmt/src/render.rs +++ b/crates/numfmt/src/render.rs @@ -233,7 +233,9 @@ fn digit_indices(toks: &[Tok], range: std::ops::Range) -> Vec { /// Renders a non-negative number through a number section. pub(crate) fn render_number(sec: &Section, v: f64) -> Out { let mut x = v.abs(); - if sec.percent > 0 { + // Observed: `%` in a scientific section is only a literal (`#%e+#` shows 123456.789 as + // `1%e+5`). + if sec.percent > 0 && !sec.exp { x *= 100f64.powi(sec.percent.min(20)); } if sec.scale > 0 { @@ -267,7 +269,9 @@ pub(crate) fn render_number(sec: &Section, v: f64) -> Out { let exp_idx = digit_indices(toks, e_at + 1..n); let step = int_idx.len().max(1) as i32; let (mut e, int_s, frac_s) = if x == 0.0 { - (0i32, String::new(), "0".repeat(frac_idx.len())) + // Observed: zero shows `0` in every integer placeholder, `#` and `?` too + // (`#.#E+#` gives `0.E+0`, `####.####E+#` gives `0000.E+0`). + (0i32, "0".repeat(int_idx.len()), "0".repeat(frac_idx.len())) } else { let (digits, xe) = decimal_digits(x); let mut e = xe - xe.rem_euclid(step); @@ -286,26 +290,30 @@ pub(crate) fn render_number(sec: &Section, v: f64) -> Out { if int_s.is_empty() && frac_s.chars().all(|c| c == '0') { e = 0; } - let mut head = String::from(if upper { "E" } else { "e" }); - if e < 0 { - head.push('-'); + let head = if upper { "E" } else { "e" }; + let sign = if e < 0 { + "-" } else if plus { - head.push('+'); - } - let width = exp_idx.iter().filter(|&&i| matches!(toks.get(i), Some(Tok::Digit('0')))).count().min(20); - let digits = format!("{:0w$}", e.unsigned_abs(), w = width); + "+" + } else { + "" + }; + let digits = e.unsigned_abs().to_string(); + // The sign sits right before the exponent digits, after any literal that follows the + // `E` (observed: `|0|e+|0|` shows `|1|e|+5|`); `?` pads the digits with spaces. match exp_idx.first() { Some(&first) => { if let Some(p) = pieces.get_mut(e_at) { - *p = head; + *p = head.to_string(); } - if let Some(p) = pieces.get_mut(first) { - *p = digits; + if let Some(b) = before.get_mut(first) { + *b = sign.to_string(); } + fill_int(toks, &exp_idx, &digits, false, false, &mut pieces); } None => { if let Some(p) = pieces.get_mut(e_at) { - *p = head + &digits; + *p = format!("{head}{sign}{digits}"); } } } diff --git a/crates/numfmt/tests/poi_number_format_approx.rs b/crates/numfmt/tests/poi_number_format_approx.rs new file mode 100644 index 0000000..0f99321 --- /dev/null +++ b/crates/numfmt/tests/poi_number_format_approx.rs @@ -0,0 +1,272 @@ +//! `TEXT(value, format)` against the results Excel cached in Apache POI's +//! `test-data/spreadsheet/NumberFormatApproxTests.xlsx` (sheet `Tests`: B = format, C = value, +//! A = `=TEXT(C, B)`). The workbook is not committed; its rows are copied here as data. + +use gridcraft_core::{DateSystem, Value}; +use gridcraft_numfmt::text_function; + +/// (format, value, Excel's result). +// The last row's 3.14159 is the workbook's value, not an approximation of pi. +#[allow(clippy::approx_constant)] +const CASES: &[(&str, f64, &str)] = &[ + (r"|#,e-#|", 123456.789, r"|1e5|"), + (r"|#e-#,|", 123456.789, r"|1e5|"), + (r"|#%e-#|", 123456.789, r"|1%e5|"), + (r"|#e-#%|", 123456.789, r"|1e5%|"), + (r"|#|e-|#|", 123456.789, r"|1|e|5|"), + (r"|#.#|e-|#|", 123456.789, r"|1.2|e|5|"), + (r"|####.####|e-|#|", 123456.789, r"|12.3457|e|4|"), + (r"|#######.####|e-|#|", 123456.789, r"|123456.789|e|0|"), + (r"|#,######.####|e-|#|", 123456.789, r"|123,456.789|e|0|"), + (r"|0|e-|0|", 123456.789, r"|1|e|5|"), + (r"|0.0|e-|0|", 123456.789, r"|1.2|e|5|"), + (r"|0000.0000|e-|0|", 123456.789, r"|0012.3457|e|4|"), + (r"|0000000.0000|e-|0|", 123456.789, r"|0123456.7890|e|0|"), + (r"|0,000000.0000|e-|0|", 123456.789, r"|0,123,456.7890|e|0|"), + (r"|?|e-|?|", 123456.789, r"|1|e|5|"), + (r"|?.?|e-|?|", 123456.789, r"|1.2|e|5|"), + (r"|????.????|e-|?|", 123456.789, r"| 12.3457|e|4|"), + (r"|???????.????|e-|?|", 123456.789, r"| 123456.789 |e|0|"), + (r"|?,??????.????|e-|?|", 123456.789, r"| 123,456.789 |e|0|"), + (r"|#,e+#|", 123456.789, r"|1e+5|"), + (r"|#e+#,|", 123456.789, r"|1e+5|"), + (r"|#%e+#|", 123456.789, r"|1%e+5|"), + (r"|#e+#%|", 123456.789, r"|1e+5%|"), + (r"|#|e+|#|", 123456.789, r"|1|e|+5|"), + (r"|#.#|e+|#|", 123456.789, r"|1.2|e|+5|"), + (r"|####.####|e+|#|", 123456.789, r"|12.3457|e|+4|"), + (r"|#######.####|e+|#|", 123456.789, r"|123456.789|e|+0|"), + (r"|#,######.####|e+|#|", 123456.789, r"|123,456.789|e|+0|"), + (r"|0|e+|0|", 123456.789, r"|1|e|+5|"), + (r"|0.0|e+|0|", 123456.789, r"|1.2|e|+5|"), + (r"|0000.0000|e+|0|", 123456.789, r"|0012.3457|e|+4|"), + (r"|0000000.0000|e+|0|", 123456.789, r"|0123456.7890|e|+0|"), + (r"|0,000000.0000|e+|0|", 123456.789, r"|0,123,456.7890|e|+0|"), + (r"|?|e+|?|", 123456.789, r"|1|e|+5|"), + (r"|?.?|e+|?|", 123456.789, r"|1.2|e|+5|"), + (r"|????.????|e+|?|", 123456.789, r"| 12.3457|e|+4|"), + (r"|???????.????|e+|?|", 123456.789, r"| 123456.789 |e|+0|"), + (r"|?,??????.????|e+|?|", 123456.789, r"| 123,456.789 |e|+0|"), + (r"|#.#|E-|#|", 123456.789, r"|1.2|E|5|"), + (r"|#.#|E+|#|", 123456.789, r"|1.2|E|+5|"), + (r"|#,e-#|", 1.23456789e-05, r"|1e-5|"), + (r"|#e-#,|", 1.23456789e-05, r"|1e-5|"), + (r"|#%e-#|", 1.23456789e-05, r"|1%e-5|"), + (r"|#e-#%|", 1.23456789e-05, r"|1e-5%|"), + (r"|#|e-|#|", 1.23456789e-05, r"|1|e|-5|"), + (r"|#.#|e-|#|", 1.23456789e-05, r"|1.2|e|-5|"), + (r"|####.####|e-|#|", 1.23456789e-05, r"|1234.5679|e|-8|"), + (r"|#######.####|e-|#|", 1.23456789e-05, r"|123.4568|e|-7|"), + (r"|#,######.####|e-|#|", 1.23456789e-05, r"|123.4568|e|-7|"), + (r"|0|e-|0|", 1.23456789e-05, r"|1|e|-5|"), + (r"|0.0|e-|0|", 1.23456789e-05, r"|1.2|e|-5|"), + (r"|0000.0000|e-|0|", 1.23456789e-05, r"|1234.5679|e|-8|"), + (r"|0000000.0000|e-|0|", 1.23456789e-05, r"|0000123.4568|e|-7|"), + (r"|0,000000.0000|e-|0|", 1.23456789e-05, r"|0,000,123.4568|e|-7|"), + (r"|?|e-|?|", 1.23456789e-05, r"|1|e|-5|"), + (r"|?.?|e-|?|", 1.23456789e-05, r"|1.2|e|-5|"), + (r"|????.????|e-|?|", 1.23456789e-05, r"|1234.5679|e|-8|"), + (r"|???????.????|e-|?|", 1.23456789e-05, r"| 123.4568|e|-7|"), + (r"|?,??????.????|e-|?|", 1.23456789e-05, r"| 123.4568|e|-7|"), + (r"|#,e+#|", 1.23456789e-05, r"|1e-5|"), + (r"|#e+#,|", 1.23456789e-05, r"|1e-5|"), + (r"|#%e+#|", 1.23456789e-05, r"|1%e-5|"), + (r"|#e+#%|", 1.23456789e-05, r"|1e-5%|"), + (r"|#|e+|#|", 1.23456789e-05, r"|1|e|-5|"), + (r"|#.#|e+|#|", 1.23456789e-05, r"|1.2|e|-5|"), + (r"|#.####|e+|#|", 0.000123456789, r"|1.2346|e|-4|"), + (r"|##.####|e+|#|", 0.000123456789, r"|1.2346|e|-4|"), + (r"|###.####|e+|#|", 0.000123456789, r"|123.4568|e|-6|"), + (r"|####.####|e+|#|", 0.000123456789, r"|1.2346|e|-4|"), + (r"|#####.####|e+|#|", 0.000123456789, r"|12.3457|e|-5|"), + (r"|#######.####|e+|#|", 0.000123456789, r"|1234.5679|e|-7|"), + (r"|#.####|e+|#|", 1.23456789e-05, r"|1.2346|e|-5|"), + (r"|##.####|e+|#|", 1.23456789e-05, r"|12.3457|e|-6|"), + (r"|###.####|e+|#|", 1.23456789e-05, r"|12.3457|e|-6|"), + (r"|####.####|e+|#|", 1.23456789e-05, r"|1234.5679|e|-8|"), + (r"|#####.####|e+|#|", 1.23456789e-05, r"|1.2346|e|-5|"), + (r"|#######.####|e+|#|", 1.23456789e-05, r"|123.4568|e|-7|"), + (r"|#.####|e+|#|", 1.23456789e-06, r"|1.2346|e|-6|"), + (r"|##.####|e+|#|", 1.23456789e-06, r"|1.2346|e|-6|"), + (r"|###.####|e+|#|", 1.23456789e-06, r"|1.2346|e|-6|"), + (r"|####.####|e+|#|", 1.23456789e-06, r"|123.4568|e|-8|"), + (r"|#####.####|e+|#|", 1.23456789e-06, r"|12345.6789|e|-10|"), + (r"|#######.####|e+|#|", 1.23456789e-06, r"|12.3457|e|-7|"), + (r"|#,######.####|e+|#|", 1.23456789e-06, r"|12.3457|e|-7|"), + (r"|0|e+|0|", 1.23456789e-05, r"|1|e|-5|"), + (r"|0.0|e+|0|", 1.23456789e-05, r"|1.2|e|-5|"), + (r"|0000.0000|e+|0|", 1.23456789e-05, r"|1234.5679|e|-8|"), + (r"|0000000.0000|e+|0|", 1.23456789e-05, r"|0000123.4568|e|-7|"), + (r"|0,000000.0000|e+|0|", 1.23456789e-05, r"|0,000,123.4568|e|-7|"), + (r"|?|e+|?|", 1.23456789e-05, r"|1|e|-5|"), + (r"|?.?|e+|?|", 1.23456789e-05, r"|1.2|e|-5|"), + (r"|????.????|e+|?|", 1.23456789e-05, r"|1234.5679|e|-8|"), + (r"|???????.????|e+|?|", 1.23456789e-05, r"| 123.4568|e|-7|"), + (r"|?,??????.????|e+|?|", 1.23456789e-05, r"| 123.4568|e|-7|"), + (r"|#.#|E-|#|", 1.23456789e-05, r"|1.2|E|-5|"), + (r"|#.#|E+|#|", 1.23456789e-05, r"|1.2|E|-5|"), + (r"|#,e-#|", 0.0, r"|0e0|"), + (r"|#e-#,|", 0.0, r"|0e0|"), + (r"|#%e-#|", 0.0, r"|0%e0|"), + (r"|#e-#%|", 0.0, r"|0e0%|"), + (r"|#|e-|#|", 0.0, r"|0|e|0|"), + (r"|#.#|e-|#|", 0.0, r"|0.|e|0|"), + (r"|####.####|e-|#|", 0.0, r"|0000.|e|0|"), + (r"|#######.####|e-|#|", 0.0, r"|0000000.|e|0|"), + (r"|#,######.####|e-|#|", 0.0, r"|0,000,000.|e|0|"), + (r"|0|e-|0|", 0.0, r"|0|e|0|"), + (r"|0.0|e-|0|", 0.0, r"|0.0|e|0|"), + (r"|0000.0000|e-|0|", 0.0, r"|0000.0000|e|0|"), + (r"|0000000.0000|e-|0|", 0.0, r"|0000000.0000|e|0|"), + (r"|0,000000.0000|e-|0|", 0.0, r"|0,000,000.0000|e|0|"), + (r"|?|e-|?|", 0.0, r"|0|e|0|"), + (r"|?.?|e-|?|", 0.0, r"|0. |e|0|"), + (r"|????.????|e-|?|", 0.0, r"|0000. |e|0|"), + (r"|???????.????|e-|?|", 0.0, r"|0000000. |e|0|"), + (r"|?,??????.????|e-|?|", 0.0, r"|0,000,000. |e|0|"), + (r"|#,e+#|", 0.0, r"|0e+0|"), + (r"|#e+#,|", 0.0, r"|0e+0|"), + (r"|#%e+#|", 0.0, r"|0%e+0|"), + (r"|#e+#%|", 0.0, r"|0e+0%|"), + (r"|#|e+|#|", 0.0, r"|0|e|+0|"), + (r"|#.#|e+|#|", 0.0, r"|0.|e|+0|"), + (r"|####.####|e+|#|", 0.0, r"|0000.|e|+0|"), + (r"|#######.####|e+|#|", 0.0, r"|0000000.|e|+0|"), + (r"|#,######.####|e+|#|", 0.0, r"|0,000,000.|e|+0|"), + (r"|0|e+|0|", 0.0, r"|0|e|+0|"), + (r"|0.0|e+|0|", 0.0, r"|0.0|e|+0|"), + (r"|0000.0000|e+|0|", 0.0, r"|0000.0000|e|+0|"), + (r"|0000000.0000|e+|0|", 0.0, r"|0000000.0000|e|+0|"), + (r"|0,000000.0000|e+|0|", 0.0, r"|0,000,000.0000|e|+0|"), + (r"|?|e+|?|", 0.0, r"|0|e|+0|"), + (r"|?.?|e+|?|", 0.0, r"|0. |e|+0|"), + (r"|????.????|e+|?|", 0.0, r"|0000. |e|+0|"), + (r"|???????.????|e+|?|", 0.0, r"|0000000. |e|+0|"), + (r"|?,??????.????|e+|?|", 0.0, r"|0,000,000. |e|+0|"), + (r"|#.#|E-|#|", 0.0, r"|0.|E|0|"), + (r"|#.#|E+|#|", 0.0, r"|0.|E|+0|"), + (r"|#,e-#|", -123456.789, r"-|1e5|"), + (r"|#e-#,|", -123456.789, r"-|1e5|"), + (r"|#%e-#|", -123456.789, r"-|1%e5|"), + (r"|#e-#%|", -123456.789, r"-|1e5%|"), + (r"|#|e-|#|", -123456.789, r"-|1|e|5|"), + (r"|#.#|e-|#|", -123456.789, r"-|1.2|e|5|"), + (r"|####.####|e-|#|", -123456.789, r"-|12.3457|e|4|"), + (r"|#######.####|e-|#|", -123456.789, r"-|123456.789|e|0|"), + (r"|#,######.####|e-|#|", -123456.789, r"-|123,456.789|e|0|"), + (r"|0|e-|0|", -123456.789, r"-|1|e|5|"), + (r"|0.0|e-|0|", -123456.789, r"-|1.2|e|5|"), + (r"|0000.0000|e-|0|", -123456.789, r"-|0012.3457|e|4|"), + (r"|0000000.0000|e-|0|", -123456.789, r"-|0123456.7890|e|0|"), + (r"|0,000000.0000|e-|0|", -123456.789, r"-|0,123,456.7890|e|0|"), + (r"|?|e-|?|", -123456.789, r"-|1|e|5|"), + (r"|?.?|e-|?|", -123456.789, r"-|1.2|e|5|"), + (r"|????.????|e-|?|", -123456.789, r"-| 12.3457|e|4|"), + (r"|???????.????|e-|?|", -123456.789, r"-| 123456.789 |e|0|"), + (r"|?,??????.????|e-|?|", -123456.789, r"-| 123,456.789 |e|0|"), + (r"|#,e+#|", -123456.789, r"-|1e+5|"), + (r"|#e+#,|", -123456.789, r"-|1e+5|"), + (r"|#%e+#|", -123456.789, r"-|1%e+5|"), + (r"|#e+#%|", -123456.789, r"-|1e+5%|"), + (r"|#|e+|#|", -123456.789, r"-|1|e|+5|"), + (r"|#.#|e+|#|", -123456.789, r"-|1.2|e|+5|"), + (r"|####.####|e+|#|", -123456.789, r"-|12.3457|e|+4|"), + (r"|#######.####|e+|#|", -123456.789, r"-|123456.789|e|+0|"), + (r"|#,######.####|e+|#|", -123456.789, r"-|123,456.789|e|+0|"), + (r"|0|e+|0|", -123456.789, r"-|1|e|+5|"), + (r"|0.0|e+|0|", -123456.789, r"-|1.2|e|+5|"), + (r"|0000.0000|e+|0|", -123456.789, r"-|0012.3457|e|+4|"), + (r"|0000000.0000|e+|0|", -123456.789, r"-|0123456.7890|e|+0|"), + (r"|0,000000.0000|e+|0|", -123456.789, r"-|0,123,456.7890|e|+0|"), + (r"|?|e+|?|", -123456.789, r"-|1|e|+5|"), + (r"|?.?|e+|?|", -123456.789, r"-|1.2|e|+5|"), + (r"|????.????|e+|?|", -123456.789, r"-| 12.3457|e|+4|"), + (r"|???????.????|e+|?|", -123456.789, r"-| 123456.789 |e|+0|"), + (r"|?,??????.????|e+|?|", -123456.789, r"-| 123,456.789 |e|+0|"), + (r"|#.#|E-|#|", -123456.789, r"-|1.2|E|5|"), + (r"|#.#|E+|#|", -123456.789, r"-|1.2|E|+5|"), + (r"|#,e-#|", -1.23456789e-05, r"-|1e-5|"), + (r"|#e-#,|", -1.23456789e-05, r"-|1e-5|"), + (r"|#%e-#|", -1.23456789e-05, r"-|1%e-5|"), + (r"|#e-#%|", -1.23456789e-05, r"-|1e-5%|"), + (r"|#|e-|#|", -1.23456789e-05, r"-|1|e|-5|"), + (r"|#.#|e-|#|", -1.23456789e-05, r"-|1.2|e|-5|"), + (r"|####.####|e-|#|", -1.23456789e-05, r"-|1234.5679|e|-8|"), + (r"|#######.####|e-|#|", -1.23456789e-05, r"-|123.4568|e|-7|"), + (r"|#,######.####|e-|#|", -1.23456789e-05, r"-|123.4568|e|-7|"), + (r"|0|e-|0|", -1.23456789e-05, r"-|1|e|-5|"), + (r"|0.0|e-|0|", -1.23456789e-05, r"-|1.2|e|-5|"), + (r"|0000.0000|e-|0|", -1.23456789e-05, r"-|1234.5679|e|-8|"), + (r"|0000000.0000|e-|0|", -1.23456789e-05, r"-|0000123.4568|e|-7|"), + (r"|0,000000.0000|e-|0|", -1.23456789e-05, r"-|0,000,123.4568|e|-7|"), + (r"|?|e-|?|", -1.23456789e-05, r"-|1|e|-5|"), + (r"|?.?|e-|?|", -1.23456789e-05, r"-|1.2|e|-5|"), + (r"|????.????|e-|?|", -1.23456789e-05, r"-|1234.5679|e|-8|"), + (r"|???????.????|e-|?|", -1.23456789e-05, r"-| 123.4568|e|-7|"), + (r"|?,??????.????|e-|?|", -1.23456789e-05, r"-| 123.4568|e|-7|"), + (r"|#,e+#|", -1.23456789e-05, r"-|1e-5|"), + (r"|#e+#,|", -1.23456789e-05, r"-|1e-5|"), + (r"|#%e+#|", -1.23456789e-05, r"-|1%e-5|"), + (r"|#e+#%|", -1.23456789e-05, r"-|1e-5%|"), + (r"|#|e+|#|", -1.23456789e-05, r"-|1|e|-5|"), + (r"|#.#|e+|#|", -1.23456789e-05, r"-|1.2|e|-5|"), + (r"|####.####|e+|#|", -1.23456789e-05, r"-|1234.5679|e|-8|"), + (r"|#######.####|e+|#|", -1.23456789e-05, r"-|123.4568|e|-7|"), + (r"|#,######.####|e+|#|", -1.23456789e-05, r"-|123.4568|e|-7|"), + (r"|0|e+|0|", -1.23456789e-05, r"-|1|e|-5|"), + (r"|0.0|e+|0|", -1.23456789e-05, r"-|1.2|e|-5|"), + (r"|0000.0000|e+|0|", -1.23456789e-05, r"-|1234.5679|e|-8|"), + (r"|0000000.0000|e+|0|", -1.23456789e-05, r"-|0000123.4568|e|-7|"), + (r"|0,000000.0000|e+|0|", -1.23456789e-05, r"-|0,000,123.4568|e|-7|"), + (r"|?|e+|?|", -1.23456789e-05, r"-|1|e|-5|"), + (r"|?.?|e+|?|", -1.23456789e-05, r"-|1.2|e|-5|"), + (r"|????.????|e+|?|", -1.23456789e-05, r"-|1234.5679|e|-8|"), + (r"|???????.????|e+|?|", -1.23456789e-05, r"-| 123.4568|e|-7|"), + (r"|?,??????.????|e+|?|", -1.23456789e-05, r"-| 123.4568|e|-7|"), + (r"|#.#|E-|#|", -1.23456789e-05, r"-|1.2|E|-5|"), + (r"|#.#|E+|#|", -1.23456789e-05, r"-|1.2|E|-5|"), + (r"|#.#|e+|#|", 1.2345678e+142, r"|1.2|e|+142|"), + (r"|#.#|e+|#####|", 1.2345678e+142, r"|1.2|e|+142|"), + (r"|#.#|e+|0|", 1.2345678e+142, r"|1.2|e|+142|"), + (r"|#.#|e+|000|", 1.2345678e+142, r"|1.2|e|+142|"), + (r"|#.#|e+|0000|", 1.2345678e+142, r"|1.2|e|+0142|"), + (r"|#.#|e+|00000|", 1.2345678e+142, r"|1.2|e|+00142|"), + (r"|#.#|e+|?|", 1.2345678e+142, r"|1.2|e|+142|"), + (r"|#.#|e+|???|", 1.2345678e+142, r"|1.2|e|+142|"), + (r"|#.#|e+|????|", 1.2345678e+142, r"|1.2|e|+ 142|"), + (r"|#.#|e+|?????|", 1.2345678e+142, r"|1.2|e|+ 142|"), + (r"|#.#|e+|#|", 0.0, r"|0.|e|+0|"), + (r"|#.#|e+|#####|", 0.0, r"|0.|e|+0|"), + (r"|#.#|e+|0|", 0.0, r"|0.|e|+0|"), + (r"|#.#|e+|000|", 0.0, r"|0.|e|+000|"), + (r"|#.#|e+|0000|", 0.0, r"|0.|e|+0000|"), + (r"|#.#|e+|00000|", 0.0, r"|0.|e|+00000|"), + (r"|#.#|e+|?|", 0.0, r"|0.|e|+0|"), + (r"|#.#|e+|???|", 0.0, r"|0.|e|+ 0|"), + (r"|#.#|e+|????|", 0.0, r"|0.|e|+ 0|"), + (r"|#.#|e+|?????|", 0.0, r"|0.|e|+ 0|"), + (r"|#.#|e+|#|", 1.2345678e-142, r"|1.2|e|-142|"), + (r"|#.#|e+|#####|", 1.2345678e-142, r"|1.2|e|-142|"), + (r"|#.#|e+|0|", 1.2345678e-142, r"|1.2|e|-142|"), + (r"|#.#|e+|000|", 1.2345678e-142, r"|1.2|e|-142|"), + (r"|#.#|e+|0000|", 1.2345678e-142, r"|1.2|e|-0142|"), + (r"|#.#|e+|00000|", 1.2345678e-142, r"|1.2|e|-00142|"), + (r"|#.#|e+|000,00|", 1.2345678e-142, r"|1.2|e|-00142|"), + (r"|#.#|e+|?|", 1.2345678e-142, r"|1.2|e|-142|"), + (r"|#.#|e+|???|", 1.2345678e-142, r"|1.2|e|-142|"), + (r"|#.#|e+|????|", 1.2345678e-142, r"|1.2|e|- 142|"), + (r"|#.#|e+|?????|", 1.2345678e-142, r"|1.2|e|- 142|"), + (r"#,##0.00e+00", 3.14159, r"3.14e+00"), +]; + +#[test] +fn poi_number_format_approx_tests() { + let wrong: Vec = CASES + .iter() + .filter_map(|&(code, v, want)| { + let got = text_function(&Value::Number(v), code, DateSystem::D1900); + (got.as_deref() != Ok(want)).then(|| format!("TEXT({v}, {code:?}) = {got:?}, Excel {want:?}")) + }) + .collect(); + assert!(wrong.is_empty(), "{} of {} cases differ:\n{}", wrong.len(), CASES.len(), wrong.join("\n")); +}