When the documentation is wrong, and the engine says so
Every compatibility table on the internet is built by reading documentation. This one is built by running formulas, and the difference shows up in an uncomfortable place: when an engine disagrees with its vendor’s own page, it is sometimes the page that is wrong.
Our corpus asserts a documented figure only after re-deriving it independently — in exact rational arithmetic or at 50 digits, from the formula the vendor’s own page states, using a clean-room implementation of the day-count and distribution procedures written for this project and used by no engine. That discipline exists to catch our own mistakes. It also catches theirs. Across 586 executed functions we have recorded 29 places where a vendor’s published page is contradicted by its own inputs, by its own table, or by the live engine: 23 on Microsoft’s pages, 5 on Google’s, 1 on The Document Foundation’s.
This is not a scoreboard, and it is not a complaint. Function documentation is genuinely difficult: the pages are old, the examples are hand-computed, the data tables get re-rolled and the prose does not always follow. Most of the 29 are the kind of slip anyone would make. The reason to publish them is narrower and more useful: if you are checking your own migration against a vendor page, these are the specific pages that will mislead you, and each one has a citation and a returned value beside it.
The surprise
Three of the 29 were caught by an engine, not by us. Our corpus originally asserted
Microsoft’s printed 1.812462 for one of the two figures in the
T.INV.2T Remark; all four LibreOffice builds returned 1.812461, which sent us
back to the derivation, which agreed with the engine. Google’s ISDATE page
publishes TRUE for an input on which its own live engine returns FALSE. And
LibreOffice’s RAWSUBTRACT help page prints a figure that
LibreOffice’s own engine contradicts in the third significant digit. When the
documentation and the engine disagree, an executed corpus is the only thing that can tell you
which one to trust.
How a page earns a place on this list
The bar is deliberately high, because “the documentation is wrong” is an easy and lazy thing to say. A page is counted here only when it makes a factual claim that is not true on its own terms — a published result that its own inputs cannot produce, a remark that contradicts a table on the same page, a value the live engine refutes. Three large categories are deliberately not counted:
- Documentation silence. A page that names no error value, publishes no worked
example, or says nothing about locale is not wrong; it is quiet. Our corpus asserts nothing in those
places and neither does this guide. Microsoft’s withdrawal of the
SEARCHB/LEFTB/MIDBbyte-function pages is the largest example: the word “DBCS” now appears zero times in the article, which leaves us with nothing to test against, not with something to correct. - Imprecision. Loose wording, a grammatical slip, a symbol reused with two meanings
on two pages, an example printed without its leading
=. Recorded in the test files, not counted here. - Engine defects. When LibreOffice returns 0.556 where Microsoft publishes 0.555, that is an engine finding, not a documentation one. Those live on their own pages — PERCENTRANK significance and the bond and treasury family are the two largest.
Microsoft: 23
The Excel column is Microsoft’s documented behaviour as recorded in our corpus. We do not run Excel, so nothing here is a measurement of Excel — the claim being made is only that a published figure does not follow from the published inputs, and the derivation is what supports it. Where the two engines we do run agree with the derivation, that is corroboration and it is noted.
| Page | What it publishes | What is true | Google Sheets (executed) | LibreOffice Calc 25.8.7.3 (executed) |
|---|---|---|---|---|
| SUMX2MY2 | =SUMX2MY2(A2:A8,B2:B8) = -55 | Over the page’s own table, data starts in row 3; the printed range takes in the header row and only six of seven pairs, and gives -64. The two sibling pages publish A3:A9 over a byte-identical table | -64 (verbatim range), -55 (corrected) | -64 (verbatim), -55 (corrected) |
| T.INV.2T and TINV | A shared Remark citing 2.28139 and 1.812462 | Both figures are wrong. 2.28139 is a digit transposition of 2.228139; 1.812462 is a last-digit error for 1.812461 | 2.228138852 and 1.812461 | 2.228138852 and 1.812461 |
| DISC | 0.001038 for settlement 2018-07-01, maturity 2048-01-01 | Those inputs give 0.000686384169. The printed figure is reproducible only with a 2038 maturity — a stale value left behind when the dates were rolled forward | 0.000686345065 | 0.000686384169 |
| SEC | “the secant of a 45 degree angle”, result 1.90359 | 1.90359 is 1/cos(45 radians). 45 degrees would give 1.4142135624. The Remarks bullet, which tells you to convert degrees with PI()/180, is the correct half of the page | 1.90359 | 1.90359 |
| SECH | “the hyperbolic secant of a 45 degree angle” | The same unit error on the sibling page; the published figure is the radian one | see note below | 5.72504E-20 |
| TBILLEQ | Prose: DSM is “computed according to the 360 days per year basis”. Result: 0.09415 | A 30/360 count from 2008-03-31 to 2008-06-01 is 61 days and gives 0.09413. Only the actual count, 62 days, reproduces the page’s own 0.09415, so the figure is right and the sentence is wrong | 0.09415 | 0.09415 |
| TDIST | Remarks: x < 0 returns #NUM! | Two bullets later the same page publishes the identities TDIST(-x,df,1) = 1 - TDIST(x,df,1) and TDIST(-x,df,2) = TDIST(x,df,2), defining the function at negative x in the same breath as forbidding it | #NUM! | #VALUE! |
| ISPMT | Argument table: per “must be between 1 and Nper” | The Remarks on the same page say “ISPMT counts each period beginning with zero, not with one”. The two indexing rules are incompatible | -64814.8148 | -64814.8148 |
| MAXA | “Arguments that contain text or FALSE evaluate as 0” | One line later: “Empty cells and text values in the array or reference are ignored”. For a text cell inside a range the two give opposite answers | 0 | 0 |
| MINA | the identical contradictory pair | same | 0 | 0 |
| VARPA | the same contradiction, third page | Resolved the way AVERAGEA’s worked example resolves it — text counts as zero | 1.25 | 1.25 |
| DPRODUCT, DSTDEV, DSTDEVP | database range A5:E11 | Over the shared example table that range starts one row below the header, leaving the field label “Yield” unresolvable — it cannot produce any published result. The sibling pages DCOUNTA, DVAR and DVARP cite A4:E10, which works | 800, 2.96648, 2.6532998 | 800, 2.96648, 2.6532998 |
| BESSELJ | 0.329925829 | The true value is 0.32992572769… The published figure is wrong in the 7th decimal — a discrepancy about 100× larger than any rounding residue | #NAME? (Sheets has no BESSELJ) | 0.329926 |
| BESSELI | 0.981666428 | A truncation of 0.98166642857…, which rounds to 0.981666429 at nine places | #NAME? | 0.98166643 |
| BESSELK | 0.277387804 | The true value is 0.27738780045…, so the correct nine-place figure is 0.277387800 | #NAME? | 0.2773878 |
| CHISQ.INV | 3.283020286 | A truncation of 3.283020286759…, which rounds to 3.283020287 | 3.28302029 | 3.28302029 |
| DBCS | ‘=DBCS("EXCEL") equals "EXCEL"’ | The argument comes back unchanged, which is impossible under the page’s own stated conversion rule. The line is identical to one on the page for the inverse function, ASC | #NAME? | #NAME? |
| JIS | the same unchanged-argument example | Example 2 on the same page is entirely images, so nothing is quotable from it either. Our corpus derives the value instead, and LibreOffice’s JIS confirms it | #NAME? | EXCEL |
| GAUSS | a Result cell containing the literal text “=GAUSS(2)” | The example table was published unevaluated; the only usable figure is in the Description prose | 0.4772498681 | 0.477249868052 |
| AMORLINC | =AMORLINC(A2,A3,A4,A5,A6,A7,A7) | The published formula passes A7 (the rate, 0.15) where the basis argument belongs; A8 (1) is the basis in the page’s own data table. Harmless here, because 0.15 truncates to basis 0 and period 1 is unprorated | 360 | 360 |
| ODDLPRICE and ODDLYIELD | prose: the arguments come from “cells A2:A10” | The formula printed on the same page takes eight arguments from A2:A9. An off-by-one in the prose, not in the figure — both published results reproduce exactly | #NAME? (Sheets has neither) | 99.88 and 0.04519 |
| FORECAST.ETS.STAT | statistic type 6 described as “the symmetric mean absolute percentage error metric” | That is the definition of SMAPE, which is type 5. The type-6 text is a copy of the type-5 text; type 6 is MAE | #NAME? | TRUE (the structural MAE ≤ RMSE assertion holds) |
| TRIMRANGE | an equivalence table whose third row says Z10 in the operator column and E10 in its own TRIMRANGE equivalent | One of the two ranges is a slip; the rows are meant to be equivalent | #NAME? | #NAME? |
Two notes on reading that table honestly. The SECH row’s Google Sheets value sits
at a magnitude where our numeric comparison tolerance cannot separate two answers, so we do not cite it
as agreement in either direction. And TDIST’s LibreOffice cell is
#VALUE! where #NUM! is documented — that is a separate, unrelated
finding about error codes, not part of the documentation defect.
Google: 5
| Page | What it publishes | What is true | Google Sheets (executed 2026-09-01) | LibreOffice Calc 25.8.7.3 (executed) |
|---|---|---|---|---|
| ISDATE | =ISDATE("1969-20-07") → TRUE | That is only consistent with a year-day-month reading, and the page says nothing about locale. The live engine returns FALSE. Our test file predicted this outcome in writing before the run: if the engine returns FALSE, the page is the thing that is wrong | FALSE | #NAME? |
| EPOCHTODATE | a conversion recipe in the Notes that adds 25,568 | The page’s own example table maps timestamp 0 to 1/1/1970, whose serial in Sheets’ 1899-12-30 system is 25,569. The constant in the Notes is one day short. Re-fetched to rule out a transcription error on our side | 25569 | #NAME? |
| DIVIDE | Notes: “DIVIDE is equivalent to QUOTIENT.” | The same page’s headline defines DIVIDE as the / operator, and its own gloss of QUOTIENT says “without the remainder”. 7/2 is 3.5 and QUOTIENT(7,2) is 3, so the difference is 0.5, not 0 | 0.5 | #NAME? |
| AVERAGE.WEIGHTED | =AVERAGE.WEIGHTED(A1:A2, B1:B2, C1, C2) → 6.2 | In that same table column C is the Formula column: C1 holds the header word “Formula” and C2 holds another formula. The row must have meant A3 and B3, which reproduce 6.2 exactly | 6.2 (on the corrected references) | #NAME? |
| TO_PURE_NUMBER | “If value is not a number… TO_PERCENT returns value without modification.” | The rule paragraph on TO_PURE_NUMBER’s own page names a different function as its subject. TO_PERCENT has its own page carrying the same sentence correctly. Re-fetched the same day to rule out a transcription error | abc | #NAME? |
The Document Foundation: 1
| Page | What it publishes | What is true | Google Sheets (executed 2026-09-01) | LibreOffice Calc 25.8.7.3 (executed) |
|---|---|---|---|---|
| RAWSUBTRACT | “=RAWSUBTRACT(0.987654321098765, 0.9876543210987) returns 6.53921361504217E-14” | The exact IEEE-754 difference of those two literals is 6.50590692430341…E-14. RAWSUBTRACT is defined as the raw double difference, so nothing is left for an implementation to choose — and the figure differs in the third significant digit | #NAME? (Sheets has no RAWSUBTRACT) | 6.5059069243 (×1E14) on all four builds — LibreOffice’s engine contradicts LibreOffice’s help |
That page is the only numeric example the function has, which is what makes it worth naming: there is nothing else on it to check yourself against.
One we deliberately did not count
LibreOffice’s CEILING help documents the mode-omitted case as rounding toward zero
and prints the worked example CEILING(-45.67;-2;0) = -44. All four builds actually return
-46 for =CEILING(-45.67,-2), matching Microsoft’s documented
behaviour and Google Sheets (executed 2026-07-29 and 2026-08-29 respectively). By the test used on this
page — a page contradicted by its own engine — that would qualify. We classify it instead as
a cross-engine default-behaviour question, because the mode argument is exactly the thing the two
vendors define differently and the help is describing a documented mode rather than misprinting a
figure. It is a judgement call, and we would rather show it than absorb it.
Why executed verdicts beat documentation
None of the 29 is dramatic on its own. Taken together they make a narrow point that matters for anyone doing a migration by reading:
A published figure is a claim, not a measurement. Worked examples are computed once,
by a person, and then live on a page for fifteen years while the data table around them is edited.
DISC’s stale 0.001038 and SUMX2MY2’s off-by-one range are both
that story. If you check your migration by comparing your output to a documented example, you are
comparing two things that were never guaranteed to agree.
Pages contradict themselves more often than they contradict reality. Nine of the 23 Microsoft entries are internal contradictions — a Remark against a table, a description against a result, one page against its own siblings — rather than wrong numbers. That is the failure mode documentation is most prone to, and it is invisible unless you try to satisfy both halves at once, which is what writing a test case forces you to do.
An engine is a second opinion you can actually query. The three findings in the box at the top of this page exist because a running engine disagreed with a page and something had to give. That only works if you execute; a table built from documentation would have copied the wrong number straight through, and would have looked authoritative doing it.
The derivation is the real product. Where our corpus asserts a value we re-derive it first, and every note records how — which formula, which day-count procedure, which arithmetic, which sentence on which page, and the date the page was read. That is what makes it possible to say “this figure is wrong” without simply meaning “an engine disagreed with it”. When the engines disagree with each other, the derivation is the tiebreak.
Honest limits
The count of 29 is a classification, and classifications have edges. We have applied the test described above — a factual claim untrue on its own terms — and shown the largest borderline case we excluded. Someone applying a looser test would count more; someone requiring a wrong number rather than a wrong sentence would count around a dozen. Every entry is named, so the count can be disagreed with in the open.
The Excel column throughout is Microsoft’s documented behaviour as recorded in our corpus. We
do not run Excel. This is the important caveat on this page and it cuts both ways: we cannot show that
Excel returns something other than a published figure, only that the figure does not follow from the
published inputs under an independent derivation. Where Excel’s own behaviour is the question
— as it would be for TDIST at negative x — we have no measurement and say
so.
The Google Sheets column is executed output from dated runs, because Sheets has no version number to
pin: a formula-only .xlsx carrying no cached results goes to Google Drive, is recalculated
by Google’s engine, and comes back as .xlsx for readback, with a deterministic
=1111+2222 canary in every sheet proving the recalculation happened. The Microsoft-page
rows ran on 2026-08-31, the Google-page rows and RAWSUBTRACT on 2026-09-01, and
CEILING on 2026-08-29. Sheets’ export rounds floats to ten significant digits, which
is why some values above carry fewer digits than their LibreOffice counterparts.
The LibreOffice column is executed output from Calc 25.8.7.3, and every case cited here returned identical results on 24.2.0.3, 24.8.7.2 and 25.2.0.3. Those four builds are the only ones we tested.
Vendor pages change, and several of these may be corrected after this was written. Every note in the corpus records the URL and the date the page was read — most of them 2026-08-31 — so an entry that no longer matches the live page is a fixed defect rather than a wrong claim, and we would rather hear about it than not. See our methodology for how recalculation is proven rather than assumed, and the function pages for SUMX2MY2, T.INV.2T, ISDATE and RAWSUBTRACT for the per-engine support matrices.