Summary
FormulaConverter.ToR1C1 serialises negative relative offsets using the current thread culture's NumberFormat.NegativeSign. Under cultures whose negative sign is the Unicode MINUS SIGN U+2212 (sv-SE, fi-FI, nb-NO, …), it emits e.g. RC[−1] using U+2212. The R1C1 reader (ToA1 → CellFormulaR1C1), however, only accepts the ASCII hyphen-minus - (U+002D), so it can't parse the string the writer just produced — the round-trip throws ParsingException.
Minimal repro (ClosedXML.Parser 2.0.0)
using System.Globalization;
using System.Threading;
using ClosedXML.Parser;
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("sv-SE"); // NegativeSign = U+2212
// R1C1 form of "=A1/B1" stored in cell C1:
string r1c1 = FormulaConverter.ToR1C1("A1/B1", row: 1, col: 3);
// => "RC[−2]/RC[−1]" (the offsets use U+2212, not ASCII '-')
string a1 = FormulaConverter.ToA1(r1c1, row: 1, col: 3);
// throws ClosedXML.Parser.ParsingException:
// The formula `RC[−2]/RC[−1]` wasn't parsed correctly.
// The expression `RC` was parsed, but the rest `[−2]/RC[−1]` wasn't.
Under en-US / InvariantCulture the same code emits RC[-2]/RC[-1] with ASCII - and round-trips fine.
Current vs expected
- Current: the R1C1 writer's output is culture-dependent and is not always re-readable by the R1C1 reader.
- Expected: R1C1 offsets are a serialization/machine format, so the writer should emit the invariant ASCII
- regardless of thread culture (and/or the reader should accept U+2212). Either one makes the round-trip culture-independent.
Real-world impact (via ClosedXML 0.105.0)
This surfaces when simply loading a workbook. ClosedXML expands shared formulas at load by converting the master to R1C1 and back to A1 per dependent cell, so loading any .xlsx containing a shared formula with a negative relative reference (e.g. =B2-A2 filled down a column — very common) throws on a thread whose culture uses U+2212:
ClosedXML.Parser.ParsingException: The formula `RC[−2]/RC[−1]` wasn't parsed correctly...
at ClosedXML.Parser.FormulaConverter.ToA1(String formulaR1C1, Int32 row, Int32 col)
at ClosedXML.Excel.XLWorkbook.SetCellFormula(...)
at ClosedXML.Excel.XLWorkbook.LoadCell(...)
...
at ClosedXML.Excel.XLWorkbook..ctor(Stream stream)
Environment
- ClosedXML.Parser 2.0.0 (didn't spot a related fix on
main, though it looks mid-rewrite for 3.0)
- Reproduced through ClosedXML 0.105.0
- .NET 10
- Affected cultures: any whose
NumberFormat.NegativeSign is U+2212 — e.g. sv-SE, fi-FI, nb-NO
Happy to open a PR if a fix direction is welcome — my guess is emitting the invariant - in the R1C1 reference writer is the smaller change.
Summary
FormulaConverter.ToR1C1serialises negative relative offsets using the current thread culture'sNumberFormat.NegativeSign. Under cultures whose negative sign is the Unicode MINUS SIGNU+2212(sv-SE, fi-FI, nb-NO, …), it emits e.g.RC[−1]usingU+2212. The R1C1 reader (ToA1→CellFormulaR1C1), however, only accepts the ASCII hyphen-minus-(U+002D), so it can't parse the string the writer just produced — the round-trip throwsParsingException.Minimal repro (ClosedXML.Parser 2.0.0)
Under
en-US/InvariantCulturethe same code emitsRC[-2]/RC[-1]with ASCII-and round-trips fine.Current vs expected
-regardless of thread culture (and/or the reader should acceptU+2212). Either one makes the round-trip culture-independent.Real-world impact (via ClosedXML 0.105.0)
This surfaces when simply loading a workbook. ClosedXML expands shared formulas at load by converting the master to R1C1 and back to A1 per dependent cell, so loading any
.xlsxcontaining a shared formula with a negative relative reference (e.g.=B2-A2filled down a column — very common) throws on a thread whose culture usesU+2212:Environment
main, though it looks mid-rewrite for 3.0)NumberFormat.NegativeSignisU+2212— e.g. sv-SE, fi-FI, nb-NOHappy to open a PR if a fix direction is welcome — my guess is emitting the invariant
-in the R1C1 reference writer is the smaller change.