Surfaced while verifying the fix for #68 against Excel/Google Sheets behavior.
Current behavior
When a cell is empty:
A1=0 → TRUE ✓ (matches Excel/Sheets)
A1=\"\" → FALSE ✗ (Excel/Sheets return TRUE)
This happens because resolve_cell in crates/cell-sheet-core/src/formula/eval.rs coerces an empty cell-ref to Number(0.0) before it reaches the equality logic:
Expr::CellRef(cell_ref) => {
let val = resolve_cell(sheet, (cell_ref.row, cell_ref.col));
if val == CellValue::Empty {
CellValue::Number(0.0) // <-- empty becomes 0
} else {
val
}
}
So by the time the typed-equality arm sees the operands, the empty cell is already a number, and Number(0.0) == Text(\"\") is mixed-type → FALSE.
Expected behavior
In Excel / Google Sheets, an empty cell is treated as equal to both 0 (numeric context) and \"\" (text context). That is, empty has a kind of polymorphic identity that the spreadsheet picks based on context.
Possible designs
- Stop coercing
Empty at resolve_cell; let the equality arm handle it. Inside Op::Eq | Op::Neq, treat Empty as equal to Number(0.0), equal to Text(\"\"), and equal to Empty. Other arms (arithmetic, ordering) still coerce empty to 0 explicitly. More invasive but cleaner separation.
- Special-case
Text(\"\") in the equality arm. Currently Number(0.0) = Text(\"\") is FALSE. Add a rule: Number(0.0) = Text(\"\") and vice-versa is TRUE only when one operand originated from an empty cell. Requires tracking origin — ugly.
- Promote
Text(\"\") to Empty when comparing. Localised to the equality arm: if either side is Text(\"\") and the other is Empty or Number(0.0), return TRUE. Simplest.
Option 1 is the most principled but ripples into other places that depend on the current empty-as-zero behavior. Worth a small design discussion before picking.
Acceptance criteria
Out of scope
Whether ISBLANK(A1) should distinguish empty from \"\" — separate question. (In Excel, ISBLANK(\"\") is FALSE but \"\"=A1 is TRUE for empty A1. Asymmetric.)
Surfaced while verifying the fix for #68 against Excel/Google Sheets behavior.
Current behavior
When a cell is empty:
A1=0→TRUE✓ (matches Excel/Sheets)A1=\"\"→FALSE✗ (Excel/Sheets returnTRUE)This happens because
resolve_cellincrates/cell-sheet-core/src/formula/eval.rscoerces an empty cell-ref toNumber(0.0)before it reaches the equality logic:So by the time the typed-equality arm sees the operands, the empty cell is already a number, and
Number(0.0) == Text(\"\")is mixed-type →FALSE.Expected behavior
In Excel / Google Sheets, an empty cell is treated as equal to both
0(numeric context) and\"\"(text context). That is, empty has a kind of polymorphic identity that the spreadsheet picks based on context.Possible designs
Emptyatresolve_cell; let the equality arm handle it. InsideOp::Eq | Op::Neq, treatEmptyas equal toNumber(0.0), equal toText(\"\"), and equal toEmpty. Other arms (arithmetic, ordering) still coerce empty to 0 explicitly. More invasive but cleaner separation.Text(\"\")in the equality arm. CurrentlyNumber(0.0) = Text(\"\")is FALSE. Add a rule:Number(0.0) = Text(\"\")and vice-versa is TRUE only when one operand originated from an empty cell. Requires tracking origin — ugly.Text(\"\")toEmptywhen comparing. Localised to the equality arm: if either side isText(\"\")and the other isEmptyorNumber(0.0), return TRUE. Simplest.Option 1 is the most principled but ripples into other places that depend on the current empty-as-zero behavior. Worth a small design discussion before picking.
Acceptance criteria
A1=\"\"returnsTRUEwhenA1is empty.A1=0still returnsTRUEwhenA1is empty (don't regress).\"\"=0— decide and document. (Excel: TRUE. Sheets: TRUE.)cell-sheet-corecover empty/\"\", empty/0,\"\"/0, and ensure non-empty text cells still compare correctly (e.g.A1=\"\"is FALSE whenA1contains "x").=/<>updated to mention empty/\"\"equivalence.Out of scope
Whether
ISBLANK(A1)should distinguish empty from\"\"— separate question. (In Excel,ISBLANK(\"\")is FALSE but\"\"=A1is TRUE for empty A1. Asymmetric.)