Financial functions

NPER

Matches all 2,515 recorded answers

Recorded: 2,509 in the answer bank and 6 workbook cells. How we measure this · See the Rust code

Local names in 40 languages
LanguageName in Excel
ArabicNPER
BulgarianNPER
CzechPOČET.OBDOBÍ
DanishNPER
GermanNPER
GreekNPER
English (Australia)NPER
English (United Kingdom)NPER
English (United States)NPER
SpanishNPER
EstonianNPER
FinnishNJAKSO
FrenchNPM
HebrewNPER
CroatianNPER
HungarianPER.SZÁM
IndonesianNPER
ItalianNPER
JapaneseNPER
KoreanNPER
LithuanianNPER
LatvianNPER
NorwegianANTALL_INNBET
DutchNPER
PolishNPER
Portuguese (Brazil)NPER
Portuguese (Portugal)NPER
RomanianNPER
RussianКПЕР
SlovakNPER
SlovenianNPER
Serbian (Latin)NPER
SwedishPERIODER
ThaiNPER
TurkishTAKSİT_SAYISI
UkrainianNPER
VietnameseNPER
Chinese (Simplified)NPER
Chinese (Hong Kong)NPER
Chinese (Traditional)NPER

Names from Microsoft's support site.

What it does

NPER(rate, pmt, pv, [fv], [type])

NPER(rate,pmt,pv,[fv],[type]) solves for the number of equal payment periods at a fixed periodic rate. Future value defaults to zero; type zero pays at period end and type one at the beginning. The result may be fractional.

Try it

Input cells (optional)

Example: {"B2": 3, "B3": 4}. A1 holds the formula. Other cells need values before a formula can read them.

Enter a formula, then calculate.

Runs locally in your browser with OneCalc. Its answer may differ from Excel. Use English function names and commas between arguments.

=NPER(0,-250,1000) returns 4. At zero interest, four outflows of 250 repay the positive principal. A nonzero rate can require more periods for the same payment.

Quirks

Cash-flow signs matter: a positive principal and negative payments describe repayment. Exactly zero rate is a separate branch. A tiny nonzero rate that makes the stored 1+rate round to one reaches a logarithmic denominator of zero, giving #DIV/0! in the recorded route; it must not be replaced by a small-rate approximation.

How to implement

Use R(op) for 64-bit-significand rounding followed by a binary64 store. At zero rate, return R(-R(fv+pv)/pmt) and reject a zero payment. Otherwise set tf=1 for type zero or R(1+r) for type one, then a=R(tf*pmt), num=R(a-R(fv*r)), den=R(a+R(pv*r)), and ratio=R(num/den). Reject a nonpositive or nonfinite ratio and a nonpositive stored base=R(1+r). Return R(LN(ratio)/LN(base)) using the measured worksheet logarithm; a zero denominator log gives #DIV/0!.

The zero-rate example computes -1000/-250=4. This recipe preserves the recorded stores; it does not round the period count to an integer or establish all workbook input routes.

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.

NPER · 4 lines · calc-func/src/catalogue/financial.rs on GitHub
4 nper_fn "NPER" (3, 5) [V "rate", V "pmt", V "pv", V "fv", V "type"] |s, a, sv| {
    let t = timing(s, a, 4)?;
    tv_out(s, tv::nper(num(s, a, 0)?, num(s, a, 1)?, num(s, a, 2)?, opt(s, a, 3, 0.0)?, t))
}
nper · 75 lines · calc-func/src/ported/financial_time_value_family.rs on GitHub
/// Worksheet `NPER` — bit-exact to 64-bit Excel on `x86_64` (W109 Phase 2,
/// identified by calculation-graph search; 1,729/1,729 live main-path
/// witnesses bit-exact, max ULP 0, build 20131). The body is the legacy x87
/// spill loop — every arithmetic assignment is double-rounded
/// (`RN53(RN64(op))`) and both logarithms are the x87 worksheet `ln`:
///
/// ```text
/// rate == 0 (either sign, EXACT — no epsilon band):
///     nper = RN53(RN64( -(RN53(RN64(fv+pv))) / pmt ))     (#DIV/0! on pmt=0)
/// else:
///     tf    = RN53(RN64(1 + rate·type))
///     tfp   = RN53(RN64(tf·pmt))
///     num   = RN53(RN64(tfp - RN53(RN64(fv·rate))))
///     den   = RN53(RN64(tfp + RN53(RN64(pv·rate))))
///     ratio = RN53(RN64(num/den))                          (ratio<=0 -> #NUM!)
///     nper  = RN53(RN64( ln_x87(ratio) / ln_x87(RN53(RN64(1+rate))) ))
///             (1+rate rounding to 1 -> ln=0 -> #DIV/0!, live-probed;
///              1+rate <= 0 -> #NUM!)
/// ```
///
/// Live-probed lanes: tiny nonzero rates take the MAIN path (degrading
/// numerically exactly like Excel, then #DIV/0! once `1+rate == 1`);
/// `NPER(0, 0, ..)` is `#DIV/0!`; `rate <= -1` is `#NUM!`. This replaces the
/// former portable-CR-log path and its `EPSILON` small-rate band (both ruled
/// out by the oracle bits — see DISCREPANCY_RULED_OUT_LEDGER.csv G6-08).
pub fn nper(
    periodic_rate: f64,
    payment_value: f64,
    present_value: f64,
    future_value: f64,
    timing: PaymentTiming,
) -> Result<f64, FinancialError> {
    use crate::ported::excel_numeric::{
        excel_log, excel_x87_add, excel_x87_div, excel_x87_mul, excel_x87_sub,
    };

    validate_finite(&[periodic_rate, payment_value, present_value, future_value])?;
    if periodic_rate == 0.0 {
        if payment_value == 0.0 {
            return Err(FinancialError::Div0); // live-probed: #DIV/0!
        }
        let sum = excel_x87_add(future_value, present_value);
        let result = excel_x87_div(-sum, payment_value);
        return if result.is_finite() {
            Ok(result)
        } else {
            Err(FinancialError::Num)
        };
    }
    let tf = match timing {
        PaymentTiming::EndOfPeriod => 1.0,
        PaymentTiming::BeginningOfPeriod => excel_x87_add(1.0, periodic_rate),
    };
    let tf_pmt = excel_x87_mul(tf, payment_value);
    let numerator = excel_x87_sub(tf_pmt, excel_x87_mul(future_value, periodic_rate));
    let denominator = excel_x87_add(tf_pmt, excel_x87_mul(present_value, periodic_rate));
    let ratio = excel_x87_div(numerator, denominator);
    if ratio <= 0.0 || !ratio.is_finite() {
        return Err(FinancialError::Num);
    }
    let base = excel_x87_add(1.0, periodic_rate);
    if base <= 0.0 {
        return Err(FinancialError::Num);
    }
    let ln_den = excel_log(base);
    if ln_den == 0.0 {
        return Err(FinancialError::Div0); // 1+rate rounded to 1 (live-probed)
    }
    let result = excel_x87_div(excel_log(ratio), ln_den);
    if result.is_finite() {
        Ok(result)
    } else {
        Err(FinancialError::Num)
    }
}

Kernels shared with other functions

Argument and result plumbing

  • fail catalogue/kit
  • num catalogue/kit
  • opt catalogue/kit

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 buildPlatform and CPUCaptured inputs
20131Windows, bitness not recorded, AMD Ryzen 7 4800H with Radeon Graphics1,291
20430Windows, 64-bit, CPU not recorded1,210

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.

Recorded environments and comparison details.

Excel 16.0 build 20131

Windows, bitness not recorded, AMD Ryzen 7 4800H with Radeon Graphics

1,291 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.

Excel 16.0 build 20430

Windows, 64-bit, CPU not recorded

1,210 captured inputs are indexed for this environment. Capture dates: 2026-09-29 to 2026-09-29. This is a reading count, not a comparison with another build.

Alternatives

PMT solves the payment; RATE solves the rate; PDURATION describes growth without intervening payments.

Named variants

No named variants are recorded yet.

Suggest a correction or contribute an implementation · For agents