Skip to main content

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}