MDURATION
Quirk foundCategory: Financial · Last tested 2026-09-01
Real compatibility results for the MDURATION function: executed in Excel for the web, Google Sheets and LibreOffice Calc, with desktop Excel behavior from Microsoft’s official documentation (we do not run desktop Excel — Excel for the web is a different application and is executed separately). Syntax and links to that documentation are below.
Support matrix
| Engine | Documented | Live-tested | Verdict |
|---|---|---|---|
| Excel (desktop) | Yes | No — documented only | n/a |
| Excel for the web | — | Yes (recalc, 2026-09-01) | Supported, behaves as documented |
| Google Sheets | Yes | Yes (Drive import, 2026-08-31) | Supported, behaves as documented |
| LibreOffice Calc | Yes | Yes (25.8.7.3, 2026-08-31) | Quirk found |
LibreOffice version history
We executed the same test cases under each LibreOffice release to show exactly when MDURATION’s support changed — not documentation claims, real results.
| LibreOffice version | Verdict | Tested |
|---|---|---|
| 24.2.0.3 | Quirk found | 2026-08-31 |
| 24.8.7.2 | Quirk found | 2026-08-31 |
| 25.2.0.3 | Quirk found | 2026-08-31 |
| 25.8.7.3 | Quirk found | 2026-08-31 |
Why isn't MDURATION working in LibreOffice?
MDURATION exists in LibreOffice 25.8.7.3, but it is not a drop-in match for
Excel — our executed tests found real behavioral differences (detailed in the test results on this
page). If a formula that works in Excel or Google Sheets misbehaves in LibreOffice, compare your usage
against the failing cases above before assuming your data is wrong.
Discovered quirks
-
=ROUND(MDURATION(A2,A3,A4,A5,A6,A7),3) on
LibreOffice Calc returned
5.734, but the documented/expected
result is 5.736.
Provenance
Microsoft publishes '=MDURATION(A2,A3,A4,A5,A6,A7)' with the result 5.736 for a bond settled 2008-01-01, maturing 2016-01-01, 8% coupon, 9% yield, semiannual, actual/actual basis. DERIVATION, done from the definition rather than from any finance library. Modified duration is Macaulay duration divided by (1 + yld/frequency), and Macaulay duration is the cash-flow-weighted average time to payment, discounted at the yield. Serial 39448 = 2008-01-01 (settlement) and 42370 = 2016-01-01 (maturity), 8 years at frequency 2, so there are exactly 16 semiannual coupon dates, 2008-07-01 through 2016-01-01. THE SETTLEMENT DATE FALLS EXACTLY ON A COUPON DATE, which is what makes this example computable in closed form: the days from settlement to the next coupon equal the days in that coupon period, so their ratio is 1 on every documented day-count basis that measures both with the same ruler, and the k-th cash flow sits at exactly k/2 years. With a coupon of 100 x 0.08/2 = 4 per period, 100 returned at the end, and a per-period yield of 0.09/2 = 0.045, the price per 100 face is 94.382992475446753, the Macaulay duration is 5.9937749555451836 years, and the modified duration is 5.9937749555451836 / 1.045 = 5.7356698139188359. Computed with mpmath at 50 digits, from the sixteen individual discounted cash flows, not from a closed-form annuity shortcut -- and cross-checked against LibreOffice's own PRICE and DURATION on the same bond, which return 94.3829924754 and 5.9937749555. Rounded to three places, 5.7356698139188359 is 5.736 -- Microsoft's published figure, reproduced exactly.; MISMATCH vs expected: expected 5.736, got 5.734
-
=ROUND(MDURATION(A2,A3,A4,A5,A6,A7),10) on
LibreOffice Calc returned
5.7339235771, but the documented/expected
result is 5.7356698139.
Provenance
Three published decimals cannot distinguish a correct duration from one that is off in the third significant figure, so the same computation is asserted at ten places: 5.7356698139, from the derivation on the previous case. This is the assertion that actually constrains the coupon-schedule and day-count handling.; MISMATCH vs expected: expected 5.7356698139, got 5.7339235771
-
=MDURATION(A2,A3,A4,A5,3,A7) on
LibreOffice Calc returned
#VALUE!, but the documented/expected
result is #NUM!.
Provenance
Excel documents: "If frequency is any number other than 1, 2, or 4, MDURATION returns the #NUM! error value." Three coupons a year is not one of the three permitted values.; MISMATCH vs expected: expected '#NUM!', got '#VALUE!'
-
=MDURATION(A2,A3,A4,-0.09,A6,A7) on
LibreOffice Calc returned
#VALUE!, but the documented/expected
result is #NUM!.
Provenance
Excel documents: "If yld < 0 or if coupon < 0, MDURATION returns the #NUM! error value."; MISMATCH vs expected: expected '#NUM!', got '#VALUE!'
-
=MDURATION(A2,A3,-0.08,A5,A6,A7) on
LibreOffice Calc returned
#VALUE!, but the documented/expected
result is #NUM!.
Provenance
Excel documents: "If yld < 0 or if coupon < 0, MDURATION returns the #NUM! error value." Asserted separately from the negative-yield case because the sentence names two arguments and an engine can guard one and not the other.; MISMATCH vs expected: expected '#NUM!', got '#VALUE!'
-
=MDURATION(A2,A3,A4,A5,A6,5) on
LibreOffice Calc returned
#VALUE!, but the documented/expected
result is #NUM!.
Provenance
Excel documents: "If basis < 0 or if basis > 4, MDURATION returns the #NUM! error value." The basis table stops at 4 (European 30/360).; MISMATCH vs expected: expected '#NUM!', got '#VALUE!'
-
=MDURATION(A3,A3,A4,A5,A6,A7) on
LibreOffice Calc returned
#VALUE!, but the documented/expected
result is #NUM!.
Provenance
Excel documents: "If settlement >= maturity, MDURATION returns the #NUM! error value." Equality is the boundary the >= sign includes.; MISMATCH vs expected: expected '#NUM!', got '#VALUE!'
Executed test cases
Excel for the web (executed 2026-09-01 via OneDrive recalculation)
These values come from Excel for the web, not from desktop Excel. They are two different implementations of the calculation engine, and this run measured only the web one: the corpus was uploaded to OneDrive as .xlsx, recalculated by Excel for the web on open, and downloaded again for readback. Excel for the web is a rolling service with no pinnable version, so the run is identified by its date. Where a value here disagrees with the Expected column — which is Microsoft’s documentation of the desktop product — we cannot tell you whether the web engine diverges from the desktop one or the documentation is wrong about both, because we do not run desktop Excel.
| Formula | Description | Result | Expected | Verdict |
|---|---|---|---|---|
| =ROUND(MDURATION(A2,A3,A4,A5,A6,A7),3) | Microsoft's documented worked example, rounded to the three decimals Microsoft publishes | 5.736 | 5.736ProvenanceMicrosoft publishes '=MDURATION(A2,A3,A4,A5,A6,A7)' with the result 5.736 for a bond settled 2008-01-01, maturing 2016-01-01, 8% coupon, 9% yield, semiannual, actual/actual basis. DERIVATION, done from the definition rather than from any finance library. Modified duration is Macaulay duration divided by (1 + yld/frequency), and Macaulay duration is the cash-flow-weighted average time to payment, discounted at the yield. Serial 39448 = 2008-01-01 (settlement) and 42370 = 2016-01-01 (maturity), 8 years at frequency 2, so there are exactly 16 semiannual coupon dates, 2008-07-01 through 2016-01-01. THE SETTLEMENT DATE FALLS EXACTLY ON A COUPON DATE, which is what makes this example computable in closed form: the days from settlement to the next coupon equal the days in that coupon period, so their ratio is 1 on every documented day-count basis that measures both with the same ruler, and the k-th cash flow sits at exactly k/2 years. With a coupon of 100 x 0.08/2 = 4 per period, 100 returned at the end, and a per-period yield of 0.09/2 = 0.045, the price per 100 face is 94.382992475446753, the Macaulay duration is 5.9937749555451836 years, and the modified duration is 5.9937749555451836 / 1.045 = 5.7356698139188359. Computed with mpmath at 50 digits, from the sixteen individual discounted cash flows, not from a closed-form annuity shortcut -- and cross-checked against LibreOffice's own PRICE and DURATION on the same bond, which return 94.3829924754 and 5.9937749555. Rounded to three places, 5.7356698139188359 is 5.736 -- Microsoft's published figure, reproduced exactly. |
Matched |
| =ROUND(MDURATION(A2,A3,A4,A5,A6,A7),10) | The same example carried to ten decimal places | 5.7356698139 | 5.7356698139ProvenanceThree published decimals cannot distinguish a correct duration from one that is off in the third significant figure, so the same computation is asserted at ten places: 5.7356698139, from the derivation on the previous case. This is the assertion that actually constrains the coupon-schedule and day-count handling. EXECUTED RESULT -- A SILENT WRONG ANSWER: all four LibreOffice builds return 5.7339235771, against the derived and published 5.7356698139. At the three decimals Microsoft prints, that is 5.734 where Microsoft publishes 5.736. It is a plausible-looking duration, off by 0.0017 years (about 0.03%), with no error and no warning. The defect is isolated to the actual/actual path: the SAME engine, on the SAME bond, returns exactly 5.7356698139 on basis 0 and on basis 4 (see the 30/360 case in this file), so LibreOffice's two day-count routines disagree about a bond whose settlement falls on a coupon date -- the one configuration in which they cannot legitimately disagree. LibreOffice's DURATION shows the same split on the same inputs (5.991950138 on basis 1 against 5.9937749555 on basis 0), so the error is in the shared coupon-period code, not in MDURATION's final division. |
Matched |
| =ROUND(MDURATION(A2,A3,A4,A5,A6,0),10) | The same bond on basis 0 (US 30/360), which must give the same duration | 5.7356698139 | 5.7356698139ProvenanceThe basis argument enters the documented duration calculation only through the ratio of days from settlement to the next coupon over days in that coupon period. Settlement here falls exactly ON a coupon date, so that ratio is 1 under 30/360 (180 days of 180) exactly as it is under actual/actual (182 days of 182), and the discount exponents -- and therefore the duration -- are identical. The equality is forced, not empirical, which is what makes it a useful assertion: an engine whose two day-count paths disagree on a bond where they cannot disagree has a bug in one of them, and this case and the one above between them say which. |
Matched |
| =MDURATION(A2,A3,A4,A5,3,A7) | A frequency of 3, which the page excludes | #NUM! | #NUM!ProvenanceExcel documents: "If frequency is any number other than 1, 2, or 4, MDURATION returns the #NUM! error value." Three coupons a year is not one of the three permitted values. |
Matched |
| =MDURATION(A2,A3,A4,-0.09,A6,A7) | A negative yield, which the page excludes | #NUM! | #NUM!ProvenanceExcel documents: "If yld < 0 or if coupon < 0, MDURATION returns the #NUM! error value." |
Matched |
| =MDURATION(A2,A3,-0.08,A5,A6,A7) | A negative coupon, the other half of the same documented exclusion | #NUM! | #NUM!ProvenanceExcel documents: "If yld < 0 or if coupon < 0, MDURATION returns the #NUM! error value." Asserted separately from the negative-yield case because the sentence names two arguments and an engine can guard one and not the other. |
Matched |
| =MDURATION(A2,A3,A4,A5,A6,5) | A basis of 5, one past the documented range | #NUM! | #NUM!ProvenanceExcel documents: "If basis < 0 or if basis > 4, MDURATION returns the #NUM! error value." The basis table stops at 4 (European 30/360). |
Matched |
| =MDURATION(A3,A3,A4,A5,A6,A7) | Settlement equal to maturity, which the page excludes | #NUM! | #NUM!ProvenanceExcel documents: "If settlement >= maturity, MDURATION returns the #NUM! error value." Equality is the boundary the >= sign includes. |
Matched |
| =MDURATION("not a date",A3,A4,A5,A6,A7) | A settlement date that is not a date | #VALUE! | #VALUE!ProvenanceExcel documents: "If settlement or maturity is not a valid date, MDURATION returns the #VALUE! error value." Note the page uses #VALUE! for a malformed date and #NUM! for every out-of-range argument, so the two codes are asserted separately. |
Matched |
Google Sheets (executed 2026-08-31 via Drive import)
Google Sheets is a rolling service with no pinnable version, so this run is identified by its date. The corpus was imported to Drive as .xlsx, recalculated by Sheets, and exported back for readback.
| Formula | Description | Result | Expected | Verdict |
|---|---|---|---|---|
| =ROUND(MDURATION(A2,A3,A4,A5,A6,A7),3) | Microsoft's documented worked example, rounded to the three decimals Microsoft publishes | 5.736 | 5.736ProvenanceMicrosoft publishes '=MDURATION(A2,A3,A4,A5,A6,A7)' with the result 5.736 for a bond settled 2008-01-01, maturing 2016-01-01, 8% coupon, 9% yield, semiannual, actual/actual basis. DERIVATION, done from the definition rather than from any finance library. Modified duration is Macaulay duration divided by (1 + yld/frequency), and Macaulay duration is the cash-flow-weighted average time to payment, discounted at the yield. Serial 39448 = 2008-01-01 (settlement) and 42370 = 2016-01-01 (maturity), 8 years at frequency 2, so there are exactly 16 semiannual coupon dates, 2008-07-01 through 2016-01-01. THE SETTLEMENT DATE FALLS EXACTLY ON A COUPON DATE, which is what makes this example computable in closed form: the days from settlement to the next coupon equal the days in that coupon period, so their ratio is 1 on every documented day-count basis that measures both with the same ruler, and the k-th cash flow sits at exactly k/2 years. With a coupon of 100 x 0.08/2 = 4 per period, 100 returned at the end, and a per-period yield of 0.09/2 = 0.045, the price per 100 face is 94.382992475446753, the Macaulay duration is 5.9937749555451836 years, and the modified duration is 5.9937749555451836 / 1.045 = 5.7356698139188359. Computed with mpmath at 50 digits, from the sixteen individual discounted cash flows, not from a closed-form annuity shortcut -- and cross-checked against LibreOffice's own PRICE and DURATION on the same bond, which return 94.3829924754 and 5.9937749555. Rounded to three places, 5.7356698139188359 is 5.736 -- Microsoft's published figure, reproduced exactly. |
Matched |
| =ROUND(MDURATION(A2,A3,A4,A5,A6,A7),10) | The same example carried to ten decimal places | 5.735669814 | 5.7356698139ProvenanceThree published decimals cannot distinguish a correct duration from one that is off in the third significant figure, so the same computation is asserted at ten places: 5.7356698139, from the derivation on the previous case. This is the assertion that actually constrains the coupon-schedule and day-count handling. EXECUTED RESULT -- A SILENT WRONG ANSWER: all four LibreOffice builds return 5.7339235771, against the derived and published 5.7356698139. At the three decimals Microsoft prints, that is 5.734 where Microsoft publishes 5.736. It is a plausible-looking duration, off by 0.0017 years (about 0.03%), with no error and no warning. The defect is isolated to the actual/actual path: the SAME engine, on the SAME bond, returns exactly 5.7356698139 on basis 0 and on basis 4 (see the 30/360 case in this file), so LibreOffice's two day-count routines disagree about a bond whose settlement falls on a coupon date -- the one configuration in which they cannot legitimately disagree. LibreOffice's DURATION shows the same split on the same inputs (5.991950138 on basis 1 against 5.9937749555 on basis 0), so the error is in the shared coupon-period code, not in MDURATION's final division. |
Matched |
| =ROUND(MDURATION(A2,A3,A4,A5,A6,0),10) | The same bond on basis 0 (US 30/360), which must give the same duration | 5.735669814 | 5.7356698139ProvenanceThe basis argument enters the documented duration calculation only through the ratio of days from settlement to the next coupon over days in that coupon period. Settlement here falls exactly ON a coupon date, so that ratio is 1 under 30/360 (180 days of 180) exactly as it is under actual/actual (182 days of 182), and the discount exponents -- and therefore the duration -- are identical. The equality is forced, not empirical, which is what makes it a useful assertion: an engine whose two day-count paths disagree on a bond where they cannot disagree has a bug in one of them, and this case and the one above between them say which. |
Matched |
| =MDURATION(A2,A3,A4,A5,3,A7) | A frequency of 3, which the page excludes | #NUM! | #NUM!ProvenanceExcel documents: "If frequency is any number other than 1, 2, or 4, MDURATION returns the #NUM! error value." Three coupons a year is not one of the three permitted values. |
Matched |
| =MDURATION(A2,A3,A4,-0.09,A6,A7) | A negative yield, which the page excludes | #NUM! | #NUM!ProvenanceExcel documents: "If yld < 0 or if coupon < 0, MDURATION returns the #NUM! error value." |
Matched |
| =MDURATION(A2,A3,-0.08,A5,A6,A7) | A negative coupon, the other half of the same documented exclusion | #NUM! | #NUM!ProvenanceExcel documents: "If yld < 0 or if coupon < 0, MDURATION returns the #NUM! error value." Asserted separately from the negative-yield case because the sentence names two arguments and an engine can guard one and not the other. |
Matched |
| =MDURATION(A2,A3,A4,A5,A6,5) | A basis of 5, one past the documented range | #NUM! | #NUM!ProvenanceExcel documents: "If basis < 0 or if basis > 4, MDURATION returns the #NUM! error value." The basis table stops at 4 (European 30/360). |
Matched |
| =MDURATION(A3,A3,A4,A5,A6,A7) | Settlement equal to maturity, which the page excludes | #NUM! | #NUM!ProvenanceExcel documents: "If settlement >= maturity, MDURATION returns the #NUM! error value." Equality is the boundary the >= sign includes. |
Matched |
| =MDURATION("not a date",A3,A4,A5,A6,A7) | A settlement date that is not a date | #VALUE! | #VALUE!ProvenanceExcel documents: "If settlement or maturity is not a valid date, MDURATION returns the #VALUE! error value." Note the page uses #VALUE! for a malformed date and #NUM! for every out-of-range argument, so the two codes are asserted separately. |
Matched |
LibreOffice Calc 25.8.7.3 (tested 2026-08-31)
| Formula | Description | Result | Expected | Verdict |
|---|---|---|---|---|
| =ROUND(MDURATION(A2,A3,A4,A5,A6,A7),3) | Microsoft's documented worked example, rounded to the three decimals Microsoft publishes | 5.734 | 5.736ProvenanceMicrosoft publishes '=MDURATION(A2,A3,A4,A5,A6,A7)' with the result 5.736 for a bond settled 2008-01-01, maturing 2016-01-01, 8% coupon, 9% yield, semiannual, actual/actual basis. DERIVATION, done from the definition rather than from any finance library. Modified duration is Macaulay duration divided by (1 + yld/frequency), and Macaulay duration is the cash-flow-weighted average time to payment, discounted at the yield. Serial 39448 = 2008-01-01 (settlement) and 42370 = 2016-01-01 (maturity), 8 years at frequency 2, so there are exactly 16 semiannual coupon dates, 2008-07-01 through 2016-01-01. THE SETTLEMENT DATE FALLS EXACTLY ON A COUPON DATE, which is what makes this example computable in closed form: the days from settlement to the next coupon equal the days in that coupon period, so their ratio is 1 on every documented day-count basis that measures both with the same ruler, and the k-th cash flow sits at exactly k/2 years. With a coupon of 100 x 0.08/2 = 4 per period, 100 returned at the end, and a per-period yield of 0.09/2 = 0.045, the price per 100 face is 94.382992475446753, the Macaulay duration is 5.9937749555451836 years, and the modified duration is 5.9937749555451836 / 1.045 = 5.7356698139188359. Computed with mpmath at 50 digits, from the sixteen individual discounted cash flows, not from a closed-form annuity shortcut -- and cross-checked against LibreOffice's own PRICE and DURATION on the same bond, which return 94.3829924754 and 5.9937749555. Rounded to three places, 5.7356698139188359 is 5.736 -- Microsoft's published figure, reproduced exactly. |
Mismatch |
| =ROUND(MDURATION(A2,A3,A4,A5,A6,A7),10) | The same example carried to ten decimal places | 5.7339235771 | 5.7356698139ProvenanceThree published decimals cannot distinguish a correct duration from one that is off in the third significant figure, so the same computation is asserted at ten places: 5.7356698139, from the derivation on the previous case. This is the assertion that actually constrains the coupon-schedule and day-count handling. EXECUTED RESULT -- A SILENT WRONG ANSWER: all four LibreOffice builds return 5.7339235771, against the derived and published 5.7356698139. At the three decimals Microsoft prints, that is 5.734 where Microsoft publishes 5.736. It is a plausible-looking duration, off by 0.0017 years (about 0.03%), with no error and no warning. The defect is isolated to the actual/actual path: the SAME engine, on the SAME bond, returns exactly 5.7356698139 on basis 0 and on basis 4 (see the 30/360 case in this file), so LibreOffice's two day-count routines disagree about a bond whose settlement falls on a coupon date -- the one configuration in which they cannot legitimately disagree. LibreOffice's DURATION shows the same split on the same inputs (5.991950138 on basis 1 against 5.9937749555 on basis 0), so the error is in the shared coupon-period code, not in MDURATION's final division. |
Mismatch |
| =ROUND(MDURATION(A2,A3,A4,A5,A6,0),10) | The same bond on basis 0 (US 30/360), which must give the same duration | 5.7356698139 | 5.7356698139ProvenanceThe basis argument enters the documented duration calculation only through the ratio of days from settlement to the next coupon over days in that coupon period. Settlement here falls exactly ON a coupon date, so that ratio is 1 under 30/360 (180 days of 180) exactly as it is under actual/actual (182 days of 182), and the discount exponents -- and therefore the duration -- are identical. The equality is forced, not empirical, which is what makes it a useful assertion: an engine whose two day-count paths disagree on a bond where they cannot disagree has a bug in one of them, and this case and the one above between them say which. |
Matched |
| =MDURATION(A2,A3,A4,A5,3,A7) | A frequency of 3, which the page excludes | #VALUE! | #NUM!ProvenanceExcel documents: "If frequency is any number other than 1, 2, or 4, MDURATION returns the #NUM! error value." Three coupons a year is not one of the three permitted values. |
Mismatch |
| =MDURATION(A2,A3,A4,-0.09,A6,A7) | A negative yield, which the page excludes | #VALUE! | #NUM!ProvenanceExcel documents: "If yld < 0 or if coupon < 0, MDURATION returns the #NUM! error value." |
Mismatch |
| =MDURATION(A2,A3,-0.08,A5,A6,A7) | A negative coupon, the other half of the same documented exclusion | #VALUE! | #NUM!ProvenanceExcel documents: "If yld < 0 or if coupon < 0, MDURATION returns the #NUM! error value." Asserted separately from the negative-yield case because the sentence names two arguments and an engine can guard one and not the other. |
Mismatch |
| =MDURATION(A2,A3,A4,A5,A6,5) | A basis of 5, one past the documented range | #VALUE! | #NUM!ProvenanceExcel documents: "If basis < 0 or if basis > 4, MDURATION returns the #NUM! error value." The basis table stops at 4 (European 30/360). |
Mismatch |
| =MDURATION(A3,A3,A4,A5,A6,A7) | Settlement equal to maturity, which the page excludes | #VALUE! | #NUM!ProvenanceExcel documents: "If settlement >= maturity, MDURATION returns the #NUM! error value." Equality is the boundary the >= sign includes. |
Mismatch |
| =MDURATION("not a date",A3,A4,A5,A6,A7) | A settlement date that is not a date | #VALUE! | #VALUE!ProvenanceExcel documents: "If settlement or maturity is not a valid date, MDURATION returns the #VALUE! error value." Note the page uses #VALUE! for a malformed date and #NUM! for every out-of-range argument, so the two codes are asserted separately. |
Matched |
Docs & syntax
- Excel (desktop): official documentation
- Google Sheets: official documentation
- LibreOffice Calc: official documentation
Where MDURATION behaves differently
- Bond and treasury functions after a migration: what actually breaks
Executed across 26 fixed-income functions and 161 cases: ODDFPRICE and ODDFYIELD are literal stubs in LibreOffice, TBILLPRICE prices a two-year bill, INTRATE's default basis loses a day, and MDURATION is wrong on actual/actual. 83 of 84 documented #NUM! cases arrive as #VALUE!. Google Sheets matched 127 of 161. - Every error code LibreOffice reports as #VALUE!
Across our 2,334-case executed corpus, 272 cases in 135 functions return #VALUE! in LibreOffice Calc 25.8.7.3 where Microsoft documents #NUM! (244), #N/A (15), #DIV/0! (10) or #REF! (3). Google Sheets returns the documented code on 239 of the 249 it has a function for. Identical in all four LibreOffice builds tested.