Known larger differences on 424 of 765 recorded answers
Of 765 recorded answers (734 in the answer bank and 31 workbook cells), 424 do not match: 308 differ by more than 16 ULP, 96 differ by at most 16 ULP, 20 differ in kind or value. How we measure this · See the Rust code
Local names in 40 languages
Language
Name in Excel
Arabic
IRR
Bulgarian
IRR
Czech
IRR
Danish
IA
German
IKV
Greek
IRR
English (Australia)
IRR
English (United Kingdom)
IRR
English (United States)
IRR
Spanish
TIR
Estonian
IRR
Finnish
SISÄINEN.KORKO
French
IRR
Hebrew
IRR
Croatian
IRR (internal rate of return, interna stopa povrata)
Hungarian
BMR
Indonesian
IRR
Italian
TIR.COST
Japanese
IRR
Korean
IRR
Lithuanian
IRR
Latvian
IRR
Norwegian
IR
Dutch
IR
Polish
IRR
Portuguese (Brazil)
IRR
Portuguese (Portugal)
TIR
Romanian
IRR
Russian
ВСД
Slovak
IRR
Slovenian
IRR
Serbian (Latin)
IRR
Swedish
IR
Thai
IRR
Turkish
İÇ_VERİM_ORANI
Ukrainian
IRR
Vietnamese
IRR
Chinese (Simplified)
IRR
Chinese (Hong Kong)
IRR
Chinese (Traditional)
IRR
Names from Microsoft's support site.
What it does
IRR(values, [guess])
IRR(values,[guess]) returns a periodic rate that makes an ordered cash-flow series’ present value zero. The first admitted flow occurs at time zero; later flows occur at equally spaced periods. Amounts can vary. Guess defaults to 0.1.
The mathematical equation is sum(c_i/(1+r)^i)=0, with zero-based i. A series with several sign changes can have multiple acceptable roots.
Try it
=IRR({-1000,1250}) returns about 0.25: one period turns a thousand outflow into 1250 inflow.
=IRR({-1000,0,1250}) is about 0.1180339887. The explicit zero preserves a second period, giving (1+r)^2=1.25. An ignored blank would remove that position.
=IRR({-100,230,-132},0.05) is about 0.1; a guess of 0.3 leads to about 0.2 in the current route. Both mathematical roots solve 100*(1+r)^2-230*(1+r)+132=0.
Quirks
Microsoft requires at least one positive and one negative value. It ignores text, logicals and empty cells in an array/reference, so a missing amount and numeric zero have different timing effects. Supply the flows in their intended sequence.
The current collector accepts a row or column vector and gives #REF! for a rectangle with both dimensions above one. A contained error becomes #VALUE!, whereas a whole-argument scalar error preserves its code. These are current source rules, not a claim about every Excel input route.
Microsoft documents twenty tries, accuracy within 0.00001 percent and #NUM! on nonconvergence. The current candidate has distinct two-flow and general routes; its later refinement and publication rules should not be mistaken for that entire private iteration schedule.
IRR with a direct non-number gives #NUM!, and the guess is still checked
Text or a logical typed directly as the values argument of IRR is not an error of type: "", "2", "x", "0", "-2", TRUE and FALSE all give #NUM!, never #VALUE!. The guess is read anyway, and its error wins: with any of those values, a guess of "x" gives #VALUE! and a guess of NA() gives #N/A, while 0.1 or an empty guess leaves #NUM!. A direct error in values wins over the guess: IRR(NA(),"x") is #N/A.
Formula
Excel
Mathematical value
=IRR("2") Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#NUM!
—
=IRR(TRUE) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#NUM!
—
=IRR("2","x") Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#VALUE!
—
=IRR("2",NA()) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#N/A
—
=IRR("2",) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#NUM!
—
=IRR(NA()) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#N/A
—
=IRR(NA(),"x") Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Collect admitted numbers in order and validate their signs. The current internal residual forms base=1+r with ordinary binary64 arithmetic, evaluates the runtime crt_pow(base,i) for each index, divides the corresponding flow and adds forward. Its domain excludes rates at/below -0.999999999. This residual is not the public NPV wrapper’s repeated-factor loop.
Two admitted flows
The current route iterates in q=1+r, rather than returning the closed-form ratio immediately:
Set current=1+guess, previous=1, and previous_value=F(0).
Evaluate value=F(current-1). If it differs from previous_value, use secant step value*(current-previous)/(value-previous_value). If equal, use the analytic derivative and step value/Fprime(current-1); a zero derivative fails.
Set next=current-step. If its stored bits equal current, return next-1. Otherwise move the previous state forward and repeat, for at most twenty iterations. Failure gives #NUM!.
For -1000,1250, the mathematical q is 1.25 and returned rate one quarter. The q-space iteration and final subtraction still matter to less convenient low-bit results.
More than two flows
The current bounded candidate starts from zero and guess, tries secant steps, then analytic Newton steps when a secant is unavailable, and otherwise halves a positive rate or restarts at 0.1. Its search permits 100 iterations with residual magnitude at most 1e-8. A successful seed gets up to 64 analytic Newton refinements, stopping at a stored-rate fixpoint or noncontracting step.
Publication then scans sixteen adjacent binary64 rates on each side for a smaller absolute residual. It locates both ends of the contiguous equal-absolute-residual plateau by doubling the distance in ordered-rate encodings and bisecting after leaving the plateau. If that plateau spans zero it returns zero; otherwise it returns the midpoint encoding, decoded as a rate. This is distinct from averaging the endpoint numbers.
For negative rates, the current ordered key complements all bits of a negative binary64 value; for a nonnegative value it sets the high bit. This gives a monotone key and keeps the two zero representations adjacent. Decode with the inverse transformation. Plateau search has bounded distance probes and bisections and can fail rather than scan indefinitely.
The residual producer, root-selection basins, return plateaus and remaining recorded numerical differences remain open. Matching a small residual or a mathematical closed form alone does not identify Excel’s private solver or certify this function.
Rust code
Show the CalcFunc source of this function
Quoted from the public repository DnaCalc/DnaCalc, crates calc-func and calc-fml, at commit 9e1884369a67. Comments are the source's own. A kernel shared with other functions appears once, on its module page.
18 irr_fn "IRR" (1, 2) [R"values", V"guess"] |s, a, sv| {
letmut values = resolved(s, a, 0, sv)?;
// Fn7 scalar readings: held cells keep the collector's held-value rule,// even when the range is one cell. Direct text and logicals are ignored// too. Admit the guess before these scalar collections; a direct error// and larger arrays retain their existing validation order.let scalar = values.callable_value().is_none()
&& (matches!(a[0].core_ref(), CoreRef::Reference(_))
&& !matches!(values.core_ref(), CoreRef::Array(_))
|| matches!(values.core_ref(), CoreRef::Text(_) | CoreRef::Logical(_) | CoreRef::Empty));
letmut g = None;
if scalar {
g = guess(s, a, 1, sv)?;
values = CalcValue::array(CalcArray::from_scalar(values));
}
let cashflows = match cash::collect_irr_values_from_eval(&values) {
Ok(cashflows) => cashflows,
Err(error) => returncash_err(error),
};
if !scalar {
g = guess(s, a, 1, sv)?;
}
out(s, cash::irr_kernel(&cashflows, g))
}
/// Excel `IRR` collects the values array/range with these rules (verified vs live/// Excel 16.0 b20026, identical for array constants and ranges):/// numbers are included; text, logical, and blank/empty cells are **ignored**;/// and **any** error cell maps to `#VALUE!` (the specific code is not propagated)./// A whole-argument scalar error still propagates as usual. This differs from the/// strict positional collector used by XNPV/XIRR, whose values must stay aligned/// with the parallel dates array.pub(crate) fncollect_irr_values_from_eval(value: &CalcValue) -> Result<Vec<f64>, CashflowRateEvalError> {
match value.core_ref() {
CoreRef::Number(n) => Ok(vec![n]),
CoreRef::Error(code) => Err(CashflowRateEvalError::Domain(code)),
CoreRef::Array(array) => {
let shape = array.shape();
if shape.rows > 1 && shape.cols > 1 {
returnErr(CashflowRateEvalError::Domain(WorksheetErrorCode::Ref));
}
letmut out = Vec::with_capacity(shape.rows * shape.cols);
for cell in array.iter_row_major() {
match cell.core_ref() {
CoreRef::Number(n) => out.push(n),
CoreRef::Error(_) => {
returnErr(CashflowRateEvalError::Domain(WorksheetErrorCode::Value));
}
CoreRef::Text(_)
| CoreRef::Logical(_)
| CoreRef::Empty
| CoreRef::Missing => {} // ignored, like ExcelCoreRef::Array(_) | CoreRef::Reference(_) => {
returnErr(CashflowRateEvalError::Domain(WorksheetErrorCode::Value));
}
}
}
Ok(out)
}
CoreRef::Text(_) | CoreRef::Logical(_) | CoreRef::Reference(_) => {
Err(CashflowRateEvalError::Domain(WorksheetErrorCode::Value))
}
_ => Err(CashflowRateEvalError::Domain(WorksheetErrorCode::Value)),
}
}
/// Find the end of the residual plateau by doubling the distance, then bisecting its last/// interval. Near zero, `1 + rate` can be constant over millions of rates: walking them one/// by one is unbounded in practice. There are only 64 bits in the ordered representation,/// so each end needs at most 64 distance probes and 64 bisections. A plateau with no finite/// end returns #NUM!.fn residual_plateau_end<F>(
root: u64,
residual: u64,
lower: bool,
f: &mutF,
) -> Result<u64, WorksheetErrorCode>
whereF: FnMut(f64) -> Result<f64, WorksheetErrorCode>,
{
letmut inside = root;
letmut on_plateau = |rate| matchf(rate) {
Ok(value) => Ok(value.abs().to_bits() == residual),
// A probe outside the NPV domain bounds the plateau just as a different residual// does. Bisect back into the valid interval instead of rejecting the root.Err(WorksheetErrorCode::Num) => Ok(false),
Err(code) => Err(code),
};
for bit in0..64 {
let distance = 1u64 << bit;
let outside = if lower { root.saturating_sub(distance) } else { root.saturating_add(distance) };
let candidate = rate_from_ordered(outside);
if candidate.is_finite() && on_plateau(candidate)? {
inside = outside;
continue;
}
letmut outside = outside;
for _ in0..64 {
if inside.abs_diff(outside) <= 1 {
// No finite end exists when even the last finite rate is on the plateau.if !rate_from_ordered(outside).is_finite() {
returnErr(WorksheetErrorCode::Num);
}
returnOk(inside);
}
let midpoint = inside.min(outside) + inside.abs_diff(outside) / 2;
let candidate = rate_from_ordered(midpoint);
if candidate.is_finite() && on_plateau(candidate)? {
inside = midpoint;
} else {
outside = midpoint;
}
}
returnErr(WorksheetErrorCode::Num);
}
Err(WorksheetErrorCode::Num)
}
/// Two-period cashflows have a single pole in q = 1 + rate. The recorded/// rows favor a q-space secant fixpoint, including the final subtraction./// Equal residuals use the analytic Newton step, including an initial guess of/// zero. A bounded solve keeps nonconvergence as the worksheet numeric error.fnirr_two_cashflows(cashflows: &[f64], guess: f64) -> Result<f64, WorksheetErrorCode> {
letmut current = 1.0 + guess;
letmut previous = 1.0;
letmut previous_value = periodic_npv_with_t0(0.0, cashflows)?;
for _ in0..20 {
let rate = current - 1.0;
let value = periodic_npv_with_t0(rate, cashflows)?;
let step = if value == previous_value {
let derivative = periodic_npv_derivative(rate, cashflows)?;
if derivative == 0.0 {
returnErr(WorksheetErrorCode::Num);
}
value / derivative
} else {
value * (current - previous) / (value - previous_value)
};
let next = current - step;
if next.to_bits() == current.to_bits() {
returnOk(next - 1.0);
}
previous = current;
previous_value = value;
current = next;
}
Err(WorksheetErrorCode::Num)
}
No implementation has been published with a Handbook test suite for this function.
Version differences
The recorded environments below distinguish a reading from a same-input comparison. Counts describe captured inputs in each environment; overlapping captures and comparisons are not totals of unique worksheet calls.
Excel 16.0 build
Platform and CPU
Captured inputs
20131
Windows, bitness not recorded, AMD Ryzen 7 4800H with Radeon Graphics
217
The record contains no same-input comparison between these environments. Reading different inputs on different builds does not establish either a version change or cross-build agreement.
Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZA
=IRR("2") returned #NUM!. =IRR(TRUE) returned #NUM!. 5 further recorded controls are attached. Inputs were supplied through formula literals. These are controls from this environment, not a same-input comparison with another build.
Observed formulas and results
=IRR("2") → #NUM!(formula-literal)
=IRR(TRUE) → #NUM!(formula-literal)
=IRR("2","x") → #VALUE!(formula-literal)
=IRR("2",NA()) → #N/A(formula-literal)
=IRR("2",) → #NUM!(formula-literal)
=IRR(NA()) → #N/A(formula-literal)
=IRR(NA(),"x") → #N/A(formula-literal)
Excel 16.0 build 20131
Windows, bitness not recorded, AMD Ryzen 7 4800H with Radeon Graphics
217 captured inputs are indexed for this environment. Capture dates: 2026-07-11 to 2026-07-11. This is a reading count, not a comparison with another build.
Alternatives
XIRR uses actual dates. RATE assumes one repeated payment; NPV evaluates a supplied periodic rate. MIRR uses separate financing and reinvestment rates.