Merge pull request #773 from marcoarnulfo:fix/numfmt-scientific-exponent

This commit is contained in:
Brandon Thomas 2026-10-12 23:49:29 -04:00
commit 40166aff60
3 changed files with 298 additions and 13 deletions

View File

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

View File

@ -233,7 +233,9 @@ fn digit_indices(toks: &[Tok], range: std::ops::Range<usize>) -> Vec<usize> {
/// 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}");
}
}
}

View File

@ -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<String> = 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"));
}