How we verify spreadsheet compatibility

Every Excel-for-the-web, Google Sheets and LibreOffice verdict on this site comes from actually executing the formula in a real engine and checking what came back — not from reading documentation. Desktop Microsoft Excel is the one engine we do not execute: its column is Microsoft’s documented behavior, and it is the yardstick the executed engines are measured against. Excel for the web is a separate implementation of the calculation engine, so running it tells you about the web app and not about the desktop one. This page explains the machinery, what “verified” means here, and the limits of the approach.

The execution harness

For each function we author test cases: a formula, any setup cells it needs, and the result Excel documents or produces for that input. The harness writes each case into a real .xlsx workbook with openpyxl, then runs headless LibreOffice Calc over it (soffice --convert-to xlsx), which forces a full recalculation. We reload the output and compare every result against the expected value.

The same workbooks are run through Google Sheets: uploaded to Google Drive with import-conversion on, opened as a Sheet (which recalculates every formula), then exported back to .xlsx and read with the same reader. That run is dated, not versioned — Sheets is a rolling service with no release to pin — so every Sheets result on this site is labelled with the date of the run that actually executed it (“executed <date> via Drive import”, most recently 2026-09-01) rather than given a version number. A later run that re-executes part of the corpus re-dates only the functions it covered; the rest keep the date they were executed on.

Two guards make the results trustworthy:

Version matrix

The same corpus runs against multiple LibreOffice releases — currently 24.2.0.3, 24.8.7.2, 25.2.0.3, 25.8.7.3 — which is how function pages can state a precise “supported since” release rather than a guess. Current corpus: 586 functions live-tested across 2348 executed cases per release.

Google Sheets execution caveats

The Sheets run reuses the Excel-authored workbooks, and that round trip has one remaining artifact which is not Google Sheets behavior. Where a result is explained by it, we publish Inconclusive instead of a verdict — never a red “unsupported” badge:

A #NAME? from Sheets on a formula with no storage prefix, or on a function Google does not document (TEXTSPLIT, TAKE, DROP, AGGREGATE…), is a real unsupported verdict and is published as one.

Coverage — and what we deliberately do not run

The catalog holds 600 functions. 586 of them (97.7%) have executed test cases in at least one engine. 16 of those are executed but carry no verdict, and 14 are not executed at all. The last two groups are not a backlog: each one is a decision that this harness cannot produce an honest verdict for that name, and the reason is published here rather than left as a blank. Every number and every row below is derived from the same results files the function pages are built from.

Executed, but no verdict drawn (16)

FunctionEngineWhy no verdict
AIGoogle SheetsGoogle returned the formula’s own text: the cell came back holding the string =AI(…) rather than any generated answer. A value identical to the formula that was supposed to produce it is evidence that nothing evaluated it, not evidence about what the function does — and Google’s page gates the feature behind “an eligible Google Workspace or Google AI plan.” Treating the string as a result would publish a formula as AI’s output; treating it as a failure would blame Google for an entitlement this account does not carry. Neither is supportable, so the readback is published verbatim and no verdict is drawn.
BYCOLExcel for the webExcel for the web deleted this formula instead of computing it. The uploaded workbook carries the array formula in cell A30; the downloaded package has no A30 at all, while the sheet’s arithmetic canary in Z1 came back correct — so the sheet did recalculate and the formula simply did not survive the round trip. It embeds a LAMBDA, the same construct whose storage form makes Excel for the web refuse a workbook outright. A blank left by a removed formula is not a computed result, so no verdict is drawn from it.
BYROWExcel for the webExcel for the web deleted this formula instead of computing it. The uploaded workbook carries the array formula in cell A30; the downloaded package has no A30 at all, while the sheet’s arithmetic canary in Z1 came back correct — so the sheet did recalculate and the formula simply did not survive the round trip. It embeds a LAMBDA, the same construct whose storage form makes Excel for the web refuse a workbook outright. A blank left by a removed formula is not a computed result, so no verdict is drawn from it.
DDELibreOfficeDDE resolves a link into another running application — LibreOffice’s own help describes the first argument as “the name of a server application”, and its worked example edits a linked Writer document to change the value. A flat .xlsx carries no such server and a headless conversion has nothing on the other end of the link, so the #N/A recorded below describes the absent server, not LibreOffice. Publishing it as a quirk would assert a defect this run never demonstrated, which is why the executed values are shown and no verdict is drawn from them — the same judgement batch G made about WEBSERVICE’s network #N/A.
ENCODEURLExcel for the webExcel for the web returned #VALUE! for all four cases, including =ENCODEURL("abcXYZ123"), whose argument needs no encoding at all and has no failure mode. An argument-independent error on every case, from a name the engine clearly recognises (an unknown name returns #NAME? here, as AI does), is the web application declining to provide a function rather than computing it wrongly. Our expected values come from Microsoft’s documentation of the desktop product, so the disagreement is between two applications’ feature sets, not evidence of a defect in either. The values are published as they came back and no verdict is drawn.
FILTERXMLExcel for the webExcel for the web returned #VALUE! for every case — both the two that expected a parsed value and the two that expected #VALUE!. That the latter two “matched” is a coincidence of a uniform error, not a demonstration that the function works, so they cannot carry a verdict either. As with ENCODEURL, the name resolves (an unknown name is #NAME? here) and the error does not vary with the arguments, which points at a feature the web application does not ship rather than at a calculation defect. Our expected values describe the desktop product. The values are published verbatim and no verdict is drawn.
FORECAST.ETSExcel for the webExcel for the web returned #N/A for every case of the FORECAST.ETS family — the existence probes, the value assertions and the cases that expected #NUM! or #VALUE! alike, all from the same 20-point timeline that FORECAST and FORECAST.LINEAR compute correctly on in this very run. A single error returned uniformly across arguments, dataset and error class is not a family of calculation defects; it is the exponential-smoothing feature being absent from this application. The name resolves (an unrecognised name is #NAME? here), and the expected values come from Microsoft’s documentation of the desktop product, so the disagreement is between what the two applications ship. The executed values are published exactly as they came back and no verdict is drawn from them.
FORECAST.ETS.CONFINTExcel for the webExcel for the web returned #N/A for every case of the FORECAST.ETS family — the existence probes, the value assertions and the cases that expected #NUM! or #VALUE! alike, all from the same 20-point timeline that FORECAST and FORECAST.LINEAR compute correctly on in this very run. A single error returned uniformly across arguments, dataset and error class is not a family of calculation defects; it is the exponential-smoothing feature being absent from this application. The name resolves (an unrecognised name is #NAME? here), and the expected values come from Microsoft’s documentation of the desktop product, so the disagreement is between what the two applications ship. The executed values are published exactly as they came back and no verdict is drawn from them.
FORECAST.ETS.SEASONALITYExcel for the webExcel for the web returned #N/A for every case of the FORECAST.ETS family — the existence probes, the value assertions and the cases that expected #NUM! or #VALUE! alike, all from the same 20-point timeline that FORECAST and FORECAST.LINEAR compute correctly on in this very run. A single error returned uniformly across arguments, dataset and error class is not a family of calculation defects; it is the exponential-smoothing feature being absent from this application. The name resolves (an unrecognised name is #NAME? here), and the expected values come from Microsoft’s documentation of the desktop product, so the disagreement is between what the two applications ship. The executed values are published exactly as they came back and no verdict is drawn from them.
FORECAST.ETS.STATExcel for the webExcel for the web returned #N/A for every case of the FORECAST.ETS family — the existence probes, the value assertions and the cases that expected #NUM! or #VALUE! alike, all from the same 20-point timeline that FORECAST and FORECAST.LINEAR compute correctly on in this very run. A single error returned uniformly across arguments, dataset and error class is not a family of calculation defects; it is the exponential-smoothing feature being absent from this application. The name resolves (an unrecognised name is #NAME? here), and the expected values come from Microsoft’s documentation of the desktop product, so the disagreement is between what the two applications ship. The executed values are published exactly as they came back and no verdict is drawn from them.
IMPORTDATAGoogle SheetsGoogle executed the call and returned #REF!. That is not a verdict about IMPORTDATA: the URL in the corpus formula is a deliberately inert example.com address (a real one returns an unbounded array, which would spill across the per-sheet recalculation canary and mark the whole run untrusted for a reason that has nothing to do with the engine), and the workbook reached Google as an uploaded .xlsx conversion rather than as a sheet a person opened and authorised. Either of those is enough to produce this error and this run cannot tell them apart. What it cannot do either way is show what IMPORTDATA does when the fetch succeeds — and the fetch is the documented behaviour, which is why the value is published exactly as it came back and no verdict is drawn from it.
IMPORTFEEDGoogle SheetsGoogle executed the call and returned #REF!. That is not a verdict about IMPORTFEED: the URL in the corpus formula is a deliberately inert example.com address (a real one returns an unbounded array, which would spill across the per-sheet recalculation canary and mark the whole run untrusted for a reason that has nothing to do with the engine), and the workbook reached Google as an uploaded .xlsx conversion rather than as a sheet a person opened and authorised. Either of those is enough to produce this error and this run cannot tell them apart. What it cannot do either way is show what IMPORTFEED does when the fetch succeeds — and the fetch is the documented behaviour, which is why the value is published exactly as it came back and no verdict is drawn from it.
IMPORTHTMLGoogle SheetsGoogle executed the call and returned #REF!. That is not a verdict about IMPORTHTML: the URL in the corpus formula is a deliberately inert example.com address (a real one returns an unbounded array, which would spill across the per-sheet recalculation canary and mark the whole run untrusted for a reason that has nothing to do with the engine), and the workbook reached Google as an uploaded .xlsx conversion rather than as a sheet a person opened and authorised. Either of those is enough to produce this error and this run cannot tell them apart. What it cannot do either way is show what IMPORTHTML does when the fetch succeeds — and the fetch is the documented behaviour, which is why the value is published exactly as it came back and no verdict is drawn from it.
IMPORTRANGEGoogle SheetsGoogle executed the call and returned #REF!, which is the documented consequence of an unauthorised link rather than a defect. Google’s own page states the gate: “Spreadsheets must be explicitly granted permission to pull data from other spreadsheets using IMPORTRANGE”, and the permission is granted by a human clicking Allow Access. Nobody clicked it — the corpus workbook was uploaded and converted, not opened and authorised — and the spreadsheet id in the formula is a placeholder naming no real document. The value is published exactly as it came back and no verdict is drawn from it.
IMPORTXMLGoogle SheetsGoogle executed the call and returned #REF!. That is not a verdict about IMPORTXML: the URL in the corpus formula is a deliberately inert example.com address (a real one returns an unbounded array, which would spill across the per-sheet recalculation canary and mark the whole run untrusted for a reason that has nothing to do with the engine), and the workbook reached Google as an uploaded .xlsx conversion rather than as a sheet a person opened and authorised. Either of those is enough to produce this error and this run cannot tell them apart. What it cannot do either way is show what IMPORTXML does when the fetch succeeds — and the fetch is the documented behaviour, which is why the value is published exactly as it came back and no verdict is drawn from it.
SPARKLINEGoogle SheetsGoogle executed the call and the exported cell came back empty, with the reader warning that a Sparkline Group extension it does not support was present in the file. That is exactly what this function’s test file predicted before the run: SPARKLINE “Creates a miniature chart contained within a single cell”, a drawing has no cached scalar for an .xlsx export to carry, and a blank here means this cell holds a picture — not the function returned nothing. Publishing a verdict from it and publishing an empty result from it would be wrong in opposite directions, so the readback is shown as it came and no verdict is drawn.

Not executed (14)

FunctionWhy not
CALLCalls a procedure in a DLL or code resource. The first argument names external code to load and run, so no test case is authored for it in any engine.
CUMIPMT_ADDLibreOffice’s own .xlsx export collapses this name onto CUMIPMT, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing CUMIPMT would test CUMIPMT, not CUMIPMT_ADD, so the finding is recorded and the verdict declined.
CUMPRINC_ADDLibreOffice’s own .xlsx export collapses this name onto CUMPRINC, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing CUMPRINC would test CUMPRINC, not CUMPRINC_ADD, so the finding is recorded and the verdict declined.
EFFECT_ADDLibreOffice’s own .xlsx export collapses this name onto EFFECT, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing EFFECT would test EFFECT, not EFFECT_ADD, so the finding is recorded and the verdict declined.
FORMULALibreOffice’s own .xlsx export collapses this name onto FORMULATEXT, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing FORMULATEXT would test FORMULATEXT, not FORMULA, so the finding is recorded and the verdict declined.
GCD_EXCEL2003LibreOffice’s own .xlsx export collapses this name onto GCD, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing GCD would test GCD, not GCD_EXCEL2003, so the finding is recorded and the verdict declined.
ISEVEN_ADDLibreOffice’s own .xlsx export collapses this name onto ISEVEN, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing ISEVEN would test ISEVEN, not ISEVEN_ADD, so the finding is recorded and the verdict declined.
ISODD_ADDLibreOffice’s own .xlsx export collapses this name onto ISODD, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing ISODD would test ISODD, not ISODD_ADD, so the finding is recorded and the verdict declined.
LCM_EXCEL2003LibreOffice’s own .xlsx export collapses this name onto LCM, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing LCM would test LCM, not LCM_EXCEL2003, so the finding is recorded and the verdict declined.
NOMINAL_ADDLibreOffice’s own .xlsx export collapses this name onto NOMINAL, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing NOMINAL would test NOMINAL, not NOMINAL_ADD, so the finding is recorded and the verdict declined.
REGISTER.IDReturns the register ID of a DLL procedure that has already been registered. Same external-code reason as CALL.
SKEWPLibreOffice’s own .xlsx export collapses this name onto SKEW.P, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing SKEW.P would test SKEW.P, not SKEWP, so the finding is recorded and the verdict declined.
WEBSERVICEIts documented behaviour IS the network round trip. LibreOffice does implement it (as _xlfn.WEBSERVICE), but the #N/A this harness sees describes the sandbox’s DNS rather than the engine, and publishing that as a quirk would assert a defect we never observed.
WEEKNUM_EXCEL2003LibreOffice’s own .xlsx export collapses this name onto WEEKNUM, so no OOXML token reaches it — all nine candidate spellings are #NAME? on all four builds. Writing WEEKNUM would test WEEKNUM, not WEEKNUM_EXCEL2003, so the finding is recorded and the verdict declined.

Per-engine coverage: Excel for the web (579 executed)

LibreOffice Calc and Google Sheets have executed every function this corpus has a test file for. Excel for the web has not, and the shortfall is not a shortfall in Excel — it is a limit of how we reach the web engine. We upload an .xlsx to OneDrive and let Excel for the web recalculate it on open; for 7 functions that upload could not be opened at all.

FunctionWhy Excel for the web has no verdict
ISOMITTEDNot reachable through this harness’s transport — not a statement about Excel. Excel for the web is reached by uploading an .xlsx to OneDrive and opening it, and its file-open refuses any workbook whose stored formulas carry the _xlpm./LAMBDA storage serialization (“Couldn’t Open the Workbook”, no cell-level diagnostic). Proven by bisect: the 40-function chunk carrying it was refused, the same chunk minus LAMBDA/LET/ISOMITTED opened, and those three alone were refused again. Desktop Excel implements ISOMITTED — it is Microsoft’s own function — and LibreOffice’s executed verdict for it is published below. What we cannot do is carry it into the web engine to measure, so no Excel-for-the-web verdict is drawn. The irony is the finding: Excel for the web will not open a file containing Excel’s own LAMBDA-family storage serialization.
LAMBDANot reachable through this harness’s transport — not a statement about Excel. Excel for the web is reached by uploading an .xlsx to OneDrive and opening it, and its file-open refuses any workbook whose stored formulas carry the _xlpm./LAMBDA storage serialization (“Couldn’t Open the Workbook”, no cell-level diagnostic). Proven by bisect: the 40-function chunk carrying it was refused, the same chunk minus LAMBDA/LET/ISOMITTED opened, and those three alone were refused again. Desktop Excel implements LAMBDA — it is Microsoft’s own function — and LibreOffice’s executed verdict for it is published below. What we cannot do is carry it into the web engine to measure, so no Excel-for-the-web verdict is drawn. The irony is the finding: Excel for the web will not open a file containing Excel’s own LAMBDA-family storage serialization.
LETNot reachable through this harness’s transport — not a statement about Excel. Excel for the web is reached by uploading an .xlsx to OneDrive and opening it, and its file-open refuses any workbook whose stored formulas carry the _xlpm./LAMBDA storage serialization (“Couldn’t Open the Workbook”, no cell-level diagnostic). Proven by bisect: the 40-function chunk carrying it was refused, the same chunk minus LAMBDA/LET/ISOMITTED opened, and those three alone were refused again. Desktop Excel implements LET — it is Microsoft’s own function — and LibreOffice’s executed verdict for it is published below. What we cannot do is carry it into the web engine to measure, so no Excel-for-the-web verdict is drawn. The irony is the finding: Excel for the web will not open a file containing Excel’s own LAMBDA-family storage serialization.
MAKEARRAYNot reachable through this harness’s transport — not a statement about Excel. Excel for the web is reached by uploading an .xlsx to OneDrive and opening it, and its file-open refuses any workbook whose stored formulas carry the _xlpm./LAMBDA storage serialization (“Couldn’t Open the Workbook”, no cell-level diagnostic). Proven by bisect: a probe workbook containing only MAP/MAKEARRAY/REDUCE/SCAN was refused on its own. Desktop Excel implements MAKEARRAY — it is Microsoft’s own function — and LibreOffice’s executed verdict for it is published below. What we cannot do is carry it into the web engine to measure, so no Excel-for-the-web verdict is drawn. The irony is the finding: Excel for the web will not open a file containing Excel’s own LAMBDA-family storage serialization.
MAPNot reachable through this harness’s transport — not a statement about Excel. Excel for the web is reached by uploading an .xlsx to OneDrive and opening it, and its file-open refuses any workbook whose stored formulas carry the _xlpm./LAMBDA storage serialization (“Couldn’t Open the Workbook”, no cell-level diagnostic). Proven by bisect: a probe workbook containing only MAP/MAKEARRAY/REDUCE/SCAN was refused on its own. Desktop Excel implements MAP — it is Microsoft’s own function — and LibreOffice’s executed verdict for it is published below. What we cannot do is carry it into the web engine to measure, so no Excel-for-the-web verdict is drawn. The irony is the finding: Excel for the web will not open a file containing Excel’s own LAMBDA-family storage serialization.
REDUCENot reachable through this harness’s transport — not a statement about Excel. Excel for the web is reached by uploading an .xlsx to OneDrive and opening it, and its file-open refuses any workbook whose stored formulas carry the _xlpm./LAMBDA storage serialization (“Couldn’t Open the Workbook”, no cell-level diagnostic). Proven by bisect: a probe workbook containing only MAP/MAKEARRAY/REDUCE/SCAN was refused on its own. Desktop Excel implements REDUCE — it is Microsoft’s own function — and LibreOffice’s executed verdict for it is published below. What we cannot do is carry it into the web engine to measure, so no Excel-for-the-web verdict is drawn. The irony is the finding: Excel for the web will not open a file containing Excel’s own LAMBDA-family storage serialization.
SCANNot reachable through this harness’s transport — not a statement about Excel. Excel for the web is reached by uploading an .xlsx to OneDrive and opening it, and its file-open refuses any workbook whose stored formulas carry the _xlpm./LAMBDA storage serialization (“Couldn’t Open the Workbook”, no cell-level diagnostic). Proven by bisect: a probe workbook containing only MAP/MAKEARRAY/REDUCE/SCAN was refused on its own. Desktop Excel implements SCAN — it is Microsoft’s own function — and LibreOffice’s executed verdict for it is published below. What we cannot do is carry it into the web engine to measure, so no Excel-for-the-web verdict is drawn. The irony is the finding: Excel for the web will not open a file containing Excel’s own LAMBDA-family storage serialization.

Read the table above carefully, because the obvious misreading is the opposite of the truth: every one of these functions works in Excel. LAMBDA and LET are Microsoft’s own headline additions to the formula language. What we are reporting is that Excel for the web refuses to open a file whose stored formulas carry the _xlpm./LAMBDA serialization — the serialization Excel itself invented to write those functions into .xlsx. So the honest summary is an awkward one for the product rather than for the function: Excel for the web will not open a workbook containing Excel’s own LAMBDA-family storage form, and that, not any missing capability, is why these seven rows are blank.

What the verdicts mean

How-to recipes are verified too

Every formula on a how-to recipe page runs through the same pipeline before publishing: the exact formula shown is executed in LibreOffice 25.8.7.3 with the sample data shown, and the page displays the value it actually returned. 282 of the 282 recipes have additionally been executed in Google Sheets (2026-08-30) by the same Drive-import round-trip used for the function corpus — those pages carry a second column showing what Google actually returned, including where it disagrees with LibreOffice — 265 of them returned exactly the LibreOffice values and 17 returned something different on at least one formula. That now covers the whole recipe corpus. The recipes whose worked examples need extra worksheets — formulas that resolve a tab name out of a cell value, span a run of consecutive tabs, or exist precisely to produce an error for a wrong-syntax reference — are built one workbook per check rather than packed into a shared one, so their tab names and tab order reach Google unchanged and nothing has to be renamed or rewritten to make them fit. Where Google’s importer altered a workbook anyway, the affected check is excluded from that recipe’s Sheets verdict and the page says which check and why.

Honest limitations

Reproduce or dispute a result

The entire harness, test corpus, and raw per-version results are public in the GitHub repository. Every function page shows the exact formula, inputs, and returned value for each case, so any result can be reproduced in a few minutes — and if an engine disagrees with us on your machine, an issue with your version number is the fastest way to get it fixed.