Files
gridcraft/docs/function-parity.md
T
Harrison BoothandClaude Sonnet 5.5 292b784f02 fix(calc): apply "Set precision as displayed" in calculation (#1366)
* ft-121.7.9: apply Set precision as displayed in calculation (#1343)

With the option on, a formula result (and each element of a spill) is
rounded to its cell's number format before it is stored and before other
formulas read it. Numbers typed, pasted or reformatted while it is on are
rounded at once, and turning it on through formulas.calculationOptions
{precisionAsDisplayed} rounds every number constant, as Excel does.

The rounding is NumberFormat::round_as_displayed in the numfmt crate,
which shares the renderer's digit logic (fixed, scientific, fraction).
General, date/time, text, digitless and conditional formats keep the full
value, as observed in Excel through COM. Nothing changes when the flag is
off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ft-121.7.9: round conditional formats by the sign's section under precision as displayed (#1343)

Excel does round formats with a condition when "Set precision as
displayed" is on; the PR kept them at full value. Observed through COM
with synthetic values (and in gridcraft-corpus
excel/precision-displayed/pad-on-conditional-sections.xlsx): conditions
are ignored and the sign picks the section, the first for positives and
the second for negatives. `[<1]0.00;0` shows 2+1/3 as 2 but stores 2.33,
and stores -10/3 as -3.

Two more observed details: with a condition and a single number section,
negatives keep the full value (`[<1]0.00`), unless an `@` section follows
(`[<1]0.00;@` rounds them by the first section); and a completely empty
section passes to the next one (`0.0;;0.000` stores -2.333 as -2.333,
`;0.0` stores 2.333 as 2.3), for formats with or without a condition.

Display is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* engine: recalculate spill anchors when a spill cell is reformatted under precision as displayed

round_edited_as_displayed only looked at stored-cell changes, but a spill
destination lives in Sheet::spill, so reformatting it (or its row or column) left
the anchor out of the changed set and the element unrounded. Add each spill anchor
whose range holds a changed cell or a changed row/column style to the changed set.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* numfmt: drop a cast the fraction merge made redundant

* precision as displayed: keep locked constants on protected sheets, round new sheets, fix manual-mode comment

* precision as displayed: name the input sheet in the new-sheet histogram test

* precision as displayed: use six values in the new-sheet histogram test

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:24:49 +01:00

188 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Function parity: worksheet functions and formulas
> **Last reviewed:** 2026-10-31 · **Last updated:** 2026-11-03 · **Change:** minor (GROUPBY total depth 2 or more adds subtotals; earlier: Python cells are edited in the formula bar; earlier: the Python errors `#PYTHON!`, `#BUSY!`, `#TIMEOUT!` and `#BLOCKED!`, and a Python in cells row; earlier: YEARFRAC basis 0 February-end rule, MOD quotient limit, PERCENTRANK significance; earlier: edge cases matched to Excel: base conversions, BITLSHIFT/BITRSHIFT, UNICHAR noncharacters, UPPER/LOWER/PROPER casing, FIXED/DOLLAR output, F/T/GAMMA/WEIBULL/BETA distributions, BINOM trial cap, HARMEAN; earlier: ECMA.CEILING listed with USDOLLAR as not on Microsoft's list; earlier: IMSQRT reproduces the stray real part Excel gives for negative reals) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
Excel's worksheet functions, category by category, against GridCraft's function library. Part of
[target-app-parity.md](target-app-parity.md); gaps feed [gaps.md](gaps.md).
## How this is measured
- **Excel's list (523):** Microsoft's "Excel functions (alphabetical)" support page, fetched and
parsed on 2026-10-10 (506 rows with their category and the Excel version that introduced
them), plus 14 text functions the page lists under combined entries (FIND/FINDB, LEFT/LEFTB,
LEN/LENB, MID/MIDB, REPLACE/REPLACEB, RIGHT/RIGHTB, SEARCH/SEARCHB), BETA.INV, and COPILOT and
PY (Microsoft 365 functions not on that page when fetched).
- **Ours (505):** every function spec in `crates/functions/src/*.rs` (`f!("NAME", …)`) plus the
- **Ours (503):** every function spec in `crates/functions/src/*.rs` (`f!("NAME", …)`) plus the
- **Ours (502):** every function spec in `crates/functions/src/*.rs` (`f!("NAME", …)`) plus the
- **Ours (502):** every function spec in `crates/functions/src/*.rs` (`f!("NAME", …)`) plus the
- **Ours (505):** every function spec in `crates/functions/src/*.rs` (`f!("NAME", …)`) plus the
evaluator's special forms in `crates/calc/src/eval.rs` (`SPECIAL_FUNCTIONS`: IF, IFS, CHOOSE,
OFFSET, INDIRECT, INDEX, SUBTOTAL, AGGREGATE, TEXT, LET, LAMBDA, MAP, REDUCE, SCAN, BYROW, BYCOL,
MAKEARRAY…), excluding the test-only `ADD2`. Every one is an Excel name.
- **Not on Microsoft's list:** USDOLLAR and ECMA.CEILING, legacy functions Excel still evaluates (like DOLLAR),
are in our library as Compatibility functions for files that use them. They are not counted above.
- **Working** excludes stubs that return an error by design: the 7 CUBE functions (`#N/A`, no
cube connections) and WEBSERVICE and FILTERXML (`#VALUE!`). Excel for Mac also lacks
WEBSERVICE and FILTERXML; Excel for Windows has them.
- The measurement is **presence**. Depth (edge cases matching Excel) is estimated below.
## Coverage
**Measured: 513 / 523 by name (98.1%); 504 / 523 working (96.4%).**
| Category | Excel | Ours | Working | % working | Missing |
|---|---|---|---|---|---|
| Statistical | 111 | 111 | 111 | 100% | — |
| Math and trigonometry | 80 | 79 | 79 | 99% | PERCENTOF |
| Financial | 55 | 55 | 55 | 100% | — |
| Engineering | 54 | 54 | 54 | 100% | — |
| Text | 50 | 48 | 48 | 96% | DETECTLANGUAGE, TRANSLATE |
| Lookup and reference | 40 | 36 | 36 | 90% | GETPIVOTDATA, IMAGE, RTD, TRIMRANGE |
| Compatibility | 40 | 40 | 40 | 100% | — |
| Date and time | 25 | 25 | 25 | 100% | — |
| Information | 22 | 21 | 21 | 95% | STOCKHISTORY |
| Logical | 19 | 19 | 19 | 100% | — |
| Database | 12 | 12 | 12 | 100% | — |
| Cube | 7 | 7 | 0 | 0% | (all 7 are `#N/A` stubs) |
| Web | 3 | 3 | 1 | 33% | (WEBSERVICE, FILTERXML are stubs; ENCODEURL works) |
| Add-in and Automation | 3 | 1 | 1 | 33% | CALL, REGISTER.ID |
| AI and Python | 2 | 0 | 0 | 0% | COPILOT, PY |
| **Total** | **523** | **505** | **496** | **94.8%** | 18 missing, 9 stubs |
By the Excel version that introduced them (working / total): before 2010 346 / 360, 2010
56 / 56, 2013 49 / 51, 2016 1 / 5, 2019 6 / 6, 2021 11 / 11, 2024 22 / 23, Microsoft 365 5 / 11.
Dynamic-array and LAMBDA-era functions (FILTER, SORT, UNIQUE, XLOOKUP, VSTACK, TAKE, GROUPBY,
PIVOTBY, REGEXTEST…) are all present.
FORECAST.ETS missing-point interpolation walks known points once, so filling a long gap takes
linear work in the completed timeline length (#1042). Interpolation values, nearest-value
endpoint handling, zero-fill mode and the existing 30% missing-point limit are unchanged.
This improves series preparation; it does not change the model-fitting or seasonality algorithms.
### Scope of what's missing
| Function(s) | In scope? | Estimate | Note |
|---|---|---|---|
| FORECAST.ETS family (4) | yes | 4–6 h | AAA exponential smoothing with seasonality detection; needed for Forecast Sheet too |
| ODDFPRICE, ODDFYIELD, ODDLPRICE, ODDLYIELD | yes | 2–4 h | Odd first/last period bond maths |
| GETPIVOTDATA | yes | 2–3 h | Needs the pivot engine's result cache; also the "Generate GetPivotData" option |
| IMAGE | yes | 3–5 h | Pictures in cells (see PR #105); fetching URLs needs a privacy decision |
| CUBE* (7), WEBSERVICE, FILTERXML | partly | 4–8 h | FILTERXML can work offline; CUBE needs an OLAP/data-model source |
| TRANSLATE, DETECTLANGUAGE, STOCKHISTORY, COPILOT, PY | no (cloud) | — | Microsoft cloud services; open equivalents only by owner decision |
| CALL, REGISTER.ID, RTD | no | — | Native DLL/COM calls; not portable or safe |
## Formula language
| Feature | Status |
|---|---|
| A1 and R1C1 references, absolute/relative/mixed, F4 cycling | done |
| 3-D references (`Sheet1:Sheet3!A1`) | done; deleting a span's end sheet keeps the rest (#140) |
| Structured references (`Table1[Col]`, `[#Totals]`, `@`; the unbracketed `[[#This Row],Col]` some writers emit) | done |
| Defined names (workbook and sheet scope), Create from Selection | done |
| Dynamic arrays, spill, `#SPILL!`, spill operator `A1#` | done; stale spills after a structural edit or a cleared blocker fixed (#133, #139; PRs #148, #157) |
| Recalculation after clearing large sparse ranges | every stored cell changed by Clear is reported to recalculation, without a million-coordinate cutoff; removed formula spills are cleared, and removing empty merges still unblocks spills (#1032) |
| SUBTOTAL / AGGREGATE input size | references trim to the used range; all areas, arrays and values of one call are charged against the 16-million-cell limit before any cell is read (skipped hidden, nested and error cells count), and `#NUM!` past it (#2175) |
| Implicit intersection `@`, legacy CSE array formulas | done; legacy array range fill (PR #156) |
| Implicit intersection `@`, legacy CSE array formulas | done, the entered range filled (PR #156) |
| Array lifting: an array in a scalar argument evaluates the function per element | done. The Analysis ToolPak functions (EOMONTH, EDATE, WORKDAY(.INTL), NETWORKDAYS(.INTL), YEARFRAC, WEEKNUM, the COUP*, PRICE*, YIELD*, ODD*, TBILL*, ACCRINT*, AMOR*, DISC, DURATION, CUMIPMT/CUMPRINC, EFFECT, NOMINAL, DOLLARDE/FR, XNPV/XIRR rate and guess, FVSCHEDULE principal, ISEVEN/ISODD, QUOTIENT, MROUND, FACTDOUBLE, SQRTPI, RANDBETWEEN, SERIESSUM, the base conversions, CONVERT, DELTA, GESTEP, ERF/ERFC, BESSEL*, COMPLEX and IM* except IMSUM/IMPRODUCT) give `#VALUE!` for a reference to more than one cell in a value argument, as Excel 365 does in both dynamic-array and legacy formulas; array constants and computed arrays (`A1:A3+0`) still lift, and range arguments (holidays, schedule, cash flows) and the .INTL weekend accept ranges. Difference: BYROW/BYCOL pass their row or column to the LAMBDA as values, so `BYROW(A1:B3,LAMBDA(r,EOMONTH(r,0)))` lifts where Excel gives `#VALUE!` |
| YEARFRAC basis 0, February-end start to day 31 | done: Excel 16.0 returns 31/360 for `YEARFRAC(2021-02-28,2021-03-31,0)` (0.08611111111111111); regression also covers leap day and reversed dates |
| Trim references `A1:.E10` (trailing), `A1.:E10` (leading), `A1.:.E10` (both), also on whole rows/columns (`A:.A`) | done; evaluated as `TRIMRANGE(range, code, code)`, adjusted on insert/delete; files store `_xlfn.TRIMRANGE(range,code,code)` (Excel's own file spelling unconfirmed in public docs), `_xlfn._TRO_ALL/_TRO_LEADING/_TRO_TRAILING(...)` read too |
| LET, LAMBDA, named LAMBDAs, ISOMITTED, MAP/REDUCE/SCAN/BYROW/BYCOL/MAKEARRAY | done. A LAMBDA that calls itself more than 140 times, or a formula nested past the depth limit, gives `#NUM!` (the target app allows deeper recursion, #1581). Calling a defined name that holds an error value gives that error, not `#NAME?` |
| Set precision as displayed | done: with the option on (`formulas.calculationOptions` `{precisionAsDisplayed}`, read and saved as `<calcPr fullPrecision="0">` and the `.xls` CalcPrecision record), a formula result, each element of a spill, and a number typed or reformatted are stored as the cell's number format shows them, and other formulas read the rounded value. Fixed decimals, percent, thousands scaling (`0.0,,`), fractions and scientific formats round; General, date and time, text and digitless formats keep the full value, as observed in Excel. Conditions don't pick the section that rounds: the sign does, the first section for positives and the second for negatives, so `[<1]0.00;0` stores 2+1/3 as 2.33 though it shows `2`, and -10/3 as -3; with a condition and a single number section, negatives keep the full value; a completely empty section (`0.0;;0.000`) passes to the next. Turning it on rounds every number constant (permanently); turning it off restores formulas only. Differences: Excel also rounds General constants it shows in scientific notation when the option is turned on (GridCraft leaves General alone); a `%` inside a scientific format is left unrounded; no Options dialog checkbox yet (command only) |
| Iterative calculation for circular references | done: with Enable Iterative Calculation on (`formulas.calculationOptions` `{iterative, maxIterations, maxChange}`, saved as `<calcPr iterate iterateCount iterateDelta>`), loops are evaluated again from their previous values (blank = 0) until no cell on a loop changes by Maximum Change or Maximum Iterations is reached; off, cycles give `#CIRC!`. Set in File › Options › Formulas (Enable iterative calculation, Maximum Iterations 1 to 32767, Maximum Change 0 or more; the two fields are disabled while iteration is off). Differences: GridCraft recalculates on open, so a loop that never settles (`=A1+1`) advances again each time the file is opened |
| 15-significant-digit comparison semantics | done (#57): `=0.1+0.2=0.3` is TRUE, and MATCH and COUNTIF compare the same way. Arithmetic keeps full precision, as Excel does — `=(0.1+0.2)-0.3` is 5.55E-17 |
| IMSQRT and the other IM* functions against Excel's text | IMSQRT follows Excel's polar route, so a negative real gives Excel's stray real part: `IMSQRT("-4")` is `1.22514845490862E-16+2i` (checked in Excel 16.0 on 79 inputs). Differences kept: IMSQRT of a number with a part above about 1E154 or below about 1E-155 gives the exact root (Excel gives `#NUM!` or 0); IMPOWER, IMEXP, IMSIN, IMCOS, IMSINH and IMCOSH keep exact results where Excel shows the same kind of noise (`IMPOWER("-4",2)` is `16-3.92047505570758E-15i`) or different trailing digits near multiples of pi/2; IMCSC and IMCSCH differ by one unit in the 15th digit; IMARGUMENT returns 16–17 digits where Excel returns 15; the text of IM* and COMPLEX results differs for some magnitudes (Excel writes `COMPLEX(1E-10,1)` as `0.0000000001+i` but `COMPLEX(0.000000123456789012345,1)` as `1.23456789012345E-07+i`, and an integer past 15 digits rounded and zero-padded) |
| MOD quotient limit | done: Excel 16.0 returns `#NUM!` when `ABS(number/divisor) >= 1.1259E12`; lower quotients retain the remainder |
| PERCENTRANK significance and precision | done: Excel 16.0 returns `#DIV/0!` from significance 309, and rounds to 15 significant digits for significance 15–308 |
| External workbook references (`[Book.xlsx]Sheet1!A1`) | parsed; links dropped on open, no update |
| Localized function names and `;` separators | **missing** (#45); en-US only |
| Long flat operator chains (`=A1+A2+…`, `-`, `*`, `/`, `^`, `&`, comparisons) | done: no operand limit below Excel's 8,192-character formula limit (Excel, observed: `=1+1+…` with 4,096 operands is exactly 8,192 characters with the `=` and calculates; one more character is refused). Evaluated (#484), printed (#628), stored, copied, adjusted, saved and reopened without recursing once per operand, on a 1 MiB stack (#916); written to XLSX as plain formulas, not dynamic arrays; Spanish entry accepts them |
| Error values | the seven classic errors plus `#SPILL!`, `#CALC!`, `#GETTING_DATA`, and the Python errors `#PYTHON!`, `#BUSY!`, `#TIMEOUT!`, `#BLOCKED!` done; `#FIELD!`, `#CONNECT!`, `#UNKNOWN!` missing (minor: they come from data types and cloud features). `ERROR.TYPE` gives 11 for `#BLOCKED!` as Excel does; Microsoft documents no number for `#PYTHON!`, `#BUSY!` or `#TIMEOUT!`, and GridCraft gives them 19, 20 and 21 |
| Python in cells | partial: Python cells are stored, edited (`python.set`, and Python mode in the formula bar: Ctrl+Alt+Shift+P or `=PY` then Tab), copied, moved and saved, and Excel's `_xlfn._xlws.PY` cells open with their cached results; the code doesn't run, so new or changed code shows `#BLOCKED!`. Typing `=PY(` and Enter is refused with a pointer to `python.set`, so `PY` stays counted as missing above |
## Depth (estimated)
| Function | Excel parity note |
|---|---|
| BINOM.DIST / BINOM.DIST.RANGE | Trial count is truncated, then must be at most 2,147,483,646; larger trials return `#NUM!`. |
| BETA.DIST | Density mode returns `#NUM!` at the lower or upper interval endpoint when the corresponding shape parameter is less than or equal to 1; cumulative endpoint results remain 0 and 1. |
| HARMEAN | Returns `#N/A` when arrays and references contain no numeric observations; zero and negative numeric observations still return `#NUM!`. |
**~82% ready.** The library is broad and has 96 test functions in `crates/functions` with many
assertions each, but there is no conformance suite against Excel-computed results, and user
reports keep finding edge cases: MIN() with no arguments (#138), COUNTBLANK past the used range
(#137, and past 16,384 cells #1010, fixed 10-14 for bounded references), ROUND at 15 digits and DATE(1900,2,29) (fixed 10-09), SUMIF sum-range sizing (fixed
10-09). Statistical distributions and financial functions with iterative solvers (RATE, IRR,
XIRR, YIELD) are the highest-risk for last-digit differences.
TEXT() and cell display share `crates/numfmt`; checked against Excel-cached results in
number- and text-format test workbooks (352 TEXT cells), all match:
fraction sections with literal text (`#\:#=/=#`, `#_#/#`), zero fractions, zero-padded
denominators, `?` placeholders padding the thousands separator, and booleans through the text
section (`TEXT(TRUE,";;;\@@\@")` = `@TRUE@`).
`MEDIAN` and `AGGREGATE` function number 12 use an overflow-safe midpoint for even-sized
inputs, so large finite middle values do not become `#NUM!` merely because their sum overflows (#1579).
Sorting, argument coercion and error propagation are unchanged; this is a numerical correction
under the documented median rule, not an extreme-value comparison against desktop Excel.
Work to close (16–26 h in total): the in-scope missing functions (10–16 h with the table above),
an oracle corpus of Excel-computed results per function (owner runs Excel on generated
workbooks; results checked in as data, not Excel files) and fixing what it finds (6–10 h).
## Revision history
| Date | Change | Summary |
|---|---|---|
| 2026-11-03 | minor | GROUPBY: a total depth of 2 or more adds a subtotal line for each group of the leading key columns (after the group, or before it for a negative depth) and labels the last line Grand Total; a depth past the key columns, or above 1 with `field_relationship` 1, is `#VALUE!`, as in Excel (#1456 item 1) |
| 2026-11-01 | minor | Python in cells row: Python mode in the formula bar |
| 2026-11-01 | minor | Python in cells (stored, not run): `#PYTHON!`, `#BUSY!`, `#TIMEOUT!` and `#BLOCKED!` done, with their `ERROR.TYPE` numbers; `PY` still counted as missing |
| 2026-10-31 | minor | BIN2*, HEX2* and OCT2* reject surrounding spaces and tabs in source strings with `#NUM!`, matching Excel; plain unpadded values remain valid |
| 2026-10-31 | minor | DEC2BIN, DEC2HEX and DEC2OCT validate `places` before returning negative two's-complement output, and Boolean places return `#VALUE!`, matching Excel |
| 2026-10-31 | minor | BITLSHIFT and BITRSHIFT return `0` for zero operands shifted beyond 53 bits in either direction; nonzero operands retain the `#NUM!` limit, matching Excel |
| 2026-10-31 | minor | UNICHAR returns `#N/A` for U+FDD0–U+FDEF and U+FFFE/U+FFFF on every plane; adjacent valid characters and existing invalid-range/surrogate errors remain unchanged, matching Excel |
| 2026-10-31 | minor | UPPER, LOWER and PROPER use single-character casing: `UPPER("Straße")` keeps ß, `LOWER("İ")` returns i, and `PROPER("İSTANBUL")` returns Istanbul, matching Windows Excel 16.0 build 20527 in LCID 1033 |
| 2026-10-31 | minor | FIXED and DOLLAR format the rounded number's shortest decimal representation before padding requested fractional zeroes, so 1.1 at 16 places and 1.005 at 20 places do not expose binary noise; measured in Windows Excel 16.0 build 20527, LCID 1033 |
| 2026-10-31 | minor | FIXED accepts output through 255 characters; DOLLAR accepts output through 254. Windows Excel 16.0 build 20527 (LCID 1033) confirmed the distinct cutoffs with grouped and ungrouped values, signs, currency layout and decimal places |
| 2026-10-31 | minor | DOLLAR uses the input sign for negative amounts that round to zero at nonnegative decimal places (`DOLLAR(-0.0001,0)` is `($0)`); when negative decimal places round to zero, Excel returns `$0`. Exact zero and negative zero stay unsigned; FIXED still returns `0` for `FIXED(-0.0001,0)` |
| 2026-10-31 | minor | F.DIST, F.DIST.RT, F.INV and F.INV.RT accept degrees of freedom at the 10-billion boundary; F.INV uses the complementary tail, with a gamma-tail limit when numerator degrees dominate, to preserve inverse accuracy; values above the limit remain invalid |
| 2026-10-31 | minor | T.DIST returns `#DIV/0!` in density mode and `#NUM!` in cumulative mode when degrees truncate to zero; negative integer degrees remain `#NUM!` |
| 2026-10-31 | minor | Student-t density uses an asymptotic log-gamma ratio for large degrees of freedom, avoiding cancellation while preserving bounded work; checked against Excel at 100 million and 10 billion degrees |
| 2026-10-31 | minor | T.INV probability zero returns `#DIV/0!`, while other out-of-range probabilities and invalid degrees remain `#NUM!`; T.INV.2T keeps its separate boundary behavior |
| 2026-10-31 | minor | GAMMA.DIST density at x=0 and shape 1 returns `#NUM!` for all positive scales; the cumulative branch and other shape boundaries are unchanged |
| 2026-10-31 | minor | WEIBULL.DIST returns zero at x=0 in both density and cumulative modes, including shape below one; negative x remains `#NUM!` |
| 2026-10-31 | minor | BINOM.DIST and BINOM.DIST.RANGE reject trial counts above 2,147,483,646 after truncation |
| 2026-10-31 | minor | BETA.DIST density returns `#NUM!` at the lower endpoint with alpha less than or equal to 1 and upper endpoint with beta less than or equal to 1; cumulative behavior is unchanged |
| 2026-10-31 | minor | HARMEAN returns `#N/A` when array or reference inputs contain no numbers; nonpositive numeric observations remain `#NUM!` |
| 2026-10-31 | minor | YEARFRAC basis 0 tests the end-31 rule against the original start day; MOD returns `#NUM!` from a quotient of 1.1259E12; PERCENTRANK returns `#DIV/0!` from significance 309 and 15 significant digits from 15 (Excel 16) |
| 2026-10-27 | minor | ECMA.CEILING added to the functions that are in our library but not on Microsoft's list (with USDOLLAR), so the counts don't change |
| 2026-10-26 | minor | IMSQRT uses Excel's polar route with its cosine reduction, so `IMSQRT("-4")` is `1.22514845490862E-16+2i` as in Excel (exact `2i` before); the rows list the IM* differences still open |
| 2026-10-25 | minor | LAMBDA row: a LAMBDA that calls itself more than 140 times, and a formula nested past the depth limit, give `#NUM!` (#1809; nesting gave `#CALC!` before). `=Foo(1)` where the defined name Foo holds `#N/A` gives `#N/A`, not `#NAME?` |
| 2026-10-23 | minor | Number text coercion (VALUE, arithmetic, typed entry): a dollar sign before accounting parentheses (`$(1,234)`, `$ (5)`) and a percent sign after them (`(5)%`) read as negatives; `(-5)`, `((5))`, `($-5)`, `$5%`, `$(5)%` and dates or times in parentheses are `#VALUE!`, as observed in Excel 365 |
| 2026-10-23 | minor | INDEX with only a row index on a reference of several rows and columns (`INDEX(A1:C3,2)`, `INDEX(A1:C3,0)`) is `#REF!`, as observed in Excel, so it also fails as a range endpoint and inside other functions; an empty column argument (`INDEX(A1:C3,2,)`) still gives the row. Checked that a column array with one index stays a 1×1 array (`TYPE` 64), which is what Excel returns |
| 2026-10-21 | minor | SUBTOTAL and AGGREGATE charge every argument against the evaluator's 16-million-cell limit before scanning; a value in XFD1048576 no longer makes `SUBTOTAL(9,A:XFD)` read the whole sheet (#2175) |
| 2026-10-19 | minor | LET and LAMBDA names compare case-insensitively in any script, as in Excel: `=LET(dług,100,DŁUG*2)` is 200 (was #NAME?); also LET-bound lambdas called or passed by name (`żar(2)`, `MAP(…,Λ)`), `ISOMITTED`, and formula-bar alias translation (one shared `gridcraft_formula::same_name`, which matches `ß` with `ẞ` and keeps dotless `ı` apart from `i`) |
| 2026-10-19 | minor | Long flat operator chains (#916): 4,096 operands (Excel's 8,192-character limit) no longer overflow the stack when entered, filled, adjusted or saved; chains past 64 operands are no longer saved as dynamic-array formulas; Spanish entry no longer refuses chains past 256 operands |
| 2026-10-17 | minor | 15-significant-digit comparison moved from missing to done (#57), and the resolved caveats on the 3-D reference, dynamic-array and legacy-array rows cleared (#140, #133, #139, #156) |
| 2026-10-16 | minor | FORECAST.ETS gap interpolation uses a forward-only known-point scan instead of rescanning each gap for every missing value; numerical behavior and missing-point limits are preserved (#1042) |
| 2026-10-15 | minor | Iterative calculation: Maximum Iterations and Maximum Change are set in File › Options › Formulas (dropped "no Options dialog yet") |
| 2026-10-15 | minor | MEDIAN and AGGREGATE median use an overflow-safe midpoint (#1579); regressions cover finite extremes, tiny values and dependent recalculation |
| 2026-10-15 | minor | ADDRESS quotes a sheet name the way Excel does: names that read as an A1 cell (`A1`) or an R1C1 reference (`R1C1`, `RC`) and names starting with `.` get quotes; `TRUE` / `FALSE` stay bare (#1458) |
| 2026-10-14 | minor | MATCH, XMATCH, VLOOKUP, HLOOKUP, XLOOKUP and LOOKUP compare numbers exactly in every match and search mode, as Excel 365 does: `MATCH(0.1+0.2,{0.3},0)` is `#N/A` and 0.3 sorts below 0.30000000000000004 in approximate and binary search. `=`, SWITCH and the criteria functions (COUNTIF(S), SUMIF(S), AVERAGEIF(S), MAXIFS…) keep comparing at 15 significant digits, also as observed |
| 2026-10-14 | minor | Analysis ToolPak value arguments (EOMONTH, WORKDAY, YEARFRAC, COUPDAYS, DISC, BIN2DEC, IM* and the rest, observed in Excel 365) give `#VALUE!` for a multi-cell reference instead of lifting it |
| 2026-10-13 | minor | Clear reports changed stored cells sparsely, including those beyond the first million coordinates; dependent formulas and removed spills update, while clearing empty merges still unblocks spills (#1032) |
| 2026-10-14 | minor | Set precision as displayed: results, spills and typed or reformatted numbers are stored as their format shows them when the option is on (#1343) |
| 2026-10-14 | minor | Bounded references up to 1,048,576 cells (`D3:AZ400`) keep their full shape instead of stopping at the used range, so `SUMPRODUCT(D3:AZ400*B3:B400,(A3:A400=x)*(D1:AZ1=y))` broadcasts like Excel and `ROWS`, `COUNTBLANK` and `--(r="")` count the blanks; references that run to the edge of the sheet (`A:A`) still stop at the used range (#1010) |
| 2026-10-12 | minor | USDOLLAR added as a Compatibility function (not on Microsoft's list, so the counts don't change); MIRR with no positive flow is -1, COUNTIF's bare `"<="` / `">="` skip empty strings, INDIRECT with `a1` = FALSE gives `#REF!` for A1-style text (#738) |
| 2026-10-11 | minor | TEXT / number formats fixed against Excel-cached results: fraction literals and zero fractions, `0` denominators zero padded, `?` pads the thousands separator, booleans use the text section (226 mismatches → 0 in 352 TEXT cells) |
| 2026-10-11 | minor | Iterative calculation: circular references iterate up to Maximum Iterations / Maximum Change when enabled |
| 2026-10-11 | minor | Trim-reference operators (`:.`, `.:`, `.:.`) and unbracketed `[[#This Row],Col]` done; dropped the stale "Scope of what's missing" rows for TRIMRANGE, PERCENTOF, BAHTTEXT and the trim references |
| 2026-10-11 | minor | Fixes from the cached-value oracle (`gridcraft-cli check`): MODE / MODE.SNGL / MODE.MULT give `#VALUE!` for text in a direct argument or single-cell reference; `VALUE("")` is `#VALUE!`; SUMPRODUCT with a text scalar argument is `#VALUE!`; ADDRESS leaves `[Book]Sheet` unquoted; VLOOKUP / HLOOKUP / LOOKUP / MATCH approximate match bisect like Excel on unsorted mixed-type data; XLOOKUP / XMATCH binary search compares across types in sort order; SUBTOTAL leaves out filtered rows for every function number (table and sheet AutoFilters) |
| 2026-10-11 | minor | ODDFPRICE, ODDFYIELD, ODDLPRICE, ODDLYIELD added: Financial 55 / 55; 505 / 523 by name, 496 working |
| 2026-10-11 | minor | PERCENTOF and EUROCONVERT added: 503 / 523 by name, 494 working |
| 2026-10-11 | minor | BAHTTEXT added: 502 / 523 by name, 493 working |
| 2026-10-11 | minor | TRIMRANGE added (the trim-reference operators not yet): 502 / 523 by name, 493 working |
| 2026-10-11 | minor | FORECAST.ETS, .CONFINT, .SEASONALITY, .STAT added (AAA exponential smoothing; Excel's optimiser is undocumented, so last digits on noisy data may differ): 505 / 523 by name, 496 working |
| 2026-10-10 | major | Created: 501 / 523 by name, 492 working, per category, against Microsoft's published list |