ravenrs_sheet/calc/precision.rs
1//! "Set precision as displayed" (File › Options › Advanced; `fullPrecision="0"` in `.xlsx`,
2//! CalcPrecision in `.xls`): with it on, a number is stored as its cell's number format shows it,
3//! and formulas read those rounded values. The rounding itself is the number format crate's
4//! ([`crate::numfmt::NumberFormat::round_as_displayed`]); this module looks up each cell's
5//! format, parsing every style's code once.
6
7use std::collections::HashMap;
8
9use crate::base::{CellRef, Value};
10use crate::model::{StyleId, Workbook};
11use crate::numfmt::NumberFormat;
12
13/// Rounds values to their cell's displayed precision, remembering each style's parsed format.
14/// Styles don't change while it is used (one recalculation pass, one sweep of a workbook).
15#[derive(Default)]
16pub struct DisplayedPrecision {
17 /// Parsed format per style; `None` for General, which keeps the full value.
18 formats: HashMap<StyleId, Option<NumberFormat>>,
19}
20
21impl DisplayedPrecision {
22 pub fn new() -> Self {
23 Self::default()
24 }
25
26 /// `n` as the format of `c` on sheet `sheet` displays it (the cell's own style, else its
27 /// row's, else its column's, as the grid shows it).
28 pub fn round(&mut self, wb: &Workbook, sheet: usize, c: CellRef, n: f64) -> f64 {
29 let Some(sh) = wb.sheet(sheet) else { return n };
30 let id = sh.style_id(c);
31 let fmt = self.formats.entry(id).or_insert_with(|| {
32 let f = NumberFormat::parse_cached(wb.styles.get(id).num_fmt.as_str());
33 (!f.is_general()).then_some(f)
34 });
35 match fmt {
36 Some(f) => f.round_as_displayed(n),
37 None => n,
38 }
39 }
40
41 /// Like [`DisplayedPrecision::round`] for a value: only numbers change.
42 pub fn round_value(&mut self, wb: &Workbook, sheet: usize, c: CellRef, v: Value) -> Value {
43 match v {
44 Value::Number(n) => {
45 let r = self.round(wb, sheet, c, n);
46 // Bit-equal results keep the value as it was (no needless change for undo/diffs).
47 if r.to_bits() == n.to_bits() { Value::Number(n) } else { Value::Number(r) }
48 }
49 v => v,
50 }
51 }
52}