Weighted-Average Discount Rate and Lease Term Disclosures: ASC 842 and IFRS 16

Compute and disclose the weighted-average discount rate and weighted-average remaining lease term under ASC 842-20-50-4(g): balance-weighting logic, KaTeX formulas, and decimal-precise Python.

Two small numbers near the bottom of every lease footnote carry an outsized amount of information: the weighted-average discount rate and the weighted-average remaining lease term. Together they let a reader reconstruct, to a close approximation, the financing cost baked into a lessee's balance-sheet liabilities and how long that obligation persists. Because they are single scalars distilled from an entire portfolio, they are also unusually easy to compute wrong — the error is invisible in isolation and only surfaces when an auditor recomputes the aggregate from the underlying schedules. This guide treats both figures as deterministic aggregation functions over the lease register: it carries the compliance layer (which standard mandates them, what population enters the average, and what quantity supplies the weight) and the engineering layer (decimal-exact accumulation, class segregation, and the guards that keep an expired lease from poisoning the result). It sits under the lease disclosure and financial statement reporting framework, and it consumes the same locked rates produced by discount rate determination and mapping and the same remaining balances that the effective interest method unwinds period by period.

Standard References Governing the Weighted-Average Disclosures Link to this section

The requirement is unambiguous under US GAAP and, deliberately, absent from IFRS — a divergence worth stating precisely, because a common mistake is to assume the two frameworks impose the same footnote.

  • ASC 842-20-50-4(g) requires a lessee to disclose, as of each reporting date and separately for finance leases and operating leases, the weighted-average remaining lease term (item (g)(3)) and the weighted-average discount rate (item (g)(4)). Both are point-in-time measures computed on the leases still on the balance sheet at period end. The Codification does not spell out a weighting algorithm, but the FASB Basis for Conclusions and settled practice fix the convention: the discount rate is weighted by the remaining balance of each lease liability, so a large obligation pulls the average toward its own rate.
  • ASC 842-20-50-4(a) frames these figures as part of the broader quantitative package that also carries lease cost components; the weighted-average items are the two that summarize the measurement inputs rather than the period expense.
  • IFRS 16 imposes no equivalent mandatory disclosure. IFRS 16.53 enumerates amounts recognized in profit or loss and cash flows, and IFRS 16.53(g) is the total cash outflow for leases — not a discount rate. IFRS 16.51 sets the disclosure objective (enough information for users to assess the effect of leases), and IFRS 16.59 asks for additional qualitative and quantitative information where it is relevant to that objective. Many IFRS preparers therefore voluntarily present a weighted-average incremental borrowing rate under 16.59, computed on the same balance-weighted basis, so a group with dual reporting can share one calculation.

The controls consequence is that the two figures are not typed into the footnote by hand; they are recomputed each period from the lease register, retained with the population and weights that produced them, and reconciled to the same lease liability rollforward that supports the balance-sheet caption. The remaining term that feeds the average is the accounting term still to run, set under the lease term boundary definitions, not the original contractual term.

Input / Output Specification Link to this section

Pin the aggregation function's contract before writing arithmetic. Every input is a per-lease attribute read at the reporting date; every output is a per-class scalar disclosed in the footnote.

Field Direction Type Validation rule Notes
lease_id in string non-empty, unique Keys the population and the audit log
lease_class in enum finance or operating ASC 842 discloses each class separately
discount_rate in Decimal 0 less than or equal to r less than 1 Annual rate locked at commencement or last remeasurement
remaining_liability in Decimal greater than or equal to 0 Carrying value of the lease liability at period end
remaining_term_months in int greater than 0 for active leases Months from reporting date to end of accounting term
remaining_payments in Decimal greater than or equal to 0 Undiscounted payments left, for the alternative term weight
as_of_date in date reporting date All remaining measures are taken at this date
wavg_rate out Decimal in (0, 1), 4 dp Weighted-average discount rate, per class
wavg_term_years out Decimal greater than 0, 1 dp Weighted-average remaining lease term, per class
population_count out int greater than or equal to 0 Active leases that entered each average

Two validation rules block the two defects that actually occur in production: excluding any lease whose remaining_liability is zero stops a fully amortized or terminated lease from diluting the average, and requiring remaining_term_months to be strictly positive stops an expired row from contributing a zero-term weight that silently drags the reported term down.

Formula Block: Balance-Weighted Aggregation Link to this section

Let a class (finance or operating) contain leases indexed , each with a locked annual discount rate , a remaining lease-liability carrying value , and a remaining term . The weighted-average discount rate weights each rate by that lease's remaining liability balance:

so a lease with a larger unpaid obligation exerts proportionally more influence on the reported rate. The weighted-average remaining lease term uses the same balance weight, which keeps the two disclosures internally consistent and ties both to the liability the reader sees on the balance sheet:

where is the weighted-average discount rate, the weighted-average remaining term, the remaining liability, and the remaining term of lease . The Codification does not mandate the weight for the term, so a defensible alternative weights the remaining term by the remaining undiscounted payments instead of the liability balance:

Either weight is acceptable, but the choice is an accounting policy that must be applied consistently across periods and classes; switching bases between reporting dates without disclosure is the kind of inconsistency an auditor flags. Both sums run only over leases with ; a lease that has amortized to zero has no remaining obligation to weight and is excluded from the population.

How the remaining liability balance weights each lease's discount rate A single horizontal bar spans the total remaining lease liability of a class. It is divided into three segments whose widths are proportional to each lease's remaining liability balance: a wide segment for a large lease carrying a low rate of four point five percent, a medium segment for a lease at six point zero percent, and a narrow segment for a small lease at seven point five percent. Because the wide low-rate segment dominates the total balance, the weighted-average discount rate lands near five point four percent, pulled toward the largest obligation rather than sitting at the simple average of the three rates. Remaining lease liability of the class, split by lease r = 4.5% L = 1,200,000 (50% of balance) r = 6.0% L = 800,000 (33%) 7.5% L = 400,000 (17%) segment width is proportional to each lease's remaining balance Lᵢ r̄ = Σ rᵢLᵢ / Σ Lᵢ ≈ 5.4% balance-weighted, not the 6.0% simple average of the three rates
The weighted-average discount rate is the liability-balance-weighted mean of the per-lease rates: the widest, lowest-rate obligation dominates the total balance and pulls the reported rate toward it, landing near 5.4% rather than the 6.0% simple average of 4.5%, 6.0%, and 7.5%.

Step-by-Step Python Implementation Link to this section

The following module computes both weighted averages, per class, from a lease register. It uses decimal.Decimal throughout so the numerator and denominator accumulate exactly, filters the population before any division, and returns a per-class result plus the population count retained for audit.

Step 1 — Model the register row and pin precision. Represent the rate and every monetary weight as Decimal. The remaining term is an integer month count; convert to years only at the final presentation step.

from dataclasses import dataclass
from decimal import Decimal, ROUND_HALF_UP, getcontext

getcontext().prec = 28  # audit-grade precision; avoid float drift in the sums

CENT = Decimal("0.01")


@dataclass(frozen=True)
class LeaseSnapshot:
    lease_id: str
    lease_class: str            # "finance" | "operating" (ASC 842-20-50-4(g))
    discount_rate: Decimal      # annual rate locked at commencement/remeasurement
    remaining_liability: Decimal  # carrying value at the reporting date
    remaining_term_months: int  # months left in the accounting term

Step 2 — Filter the population. Only leases with a positive remaining liability enter either average. A zero-balance lease has been fully amortized or terminated and carries no obligation to weight; leaving it in dilutes both figures toward zero.

def active_leases(register: list[LeaseSnapshot],
                  lease_class: str) -> list[LeaseSnapshot]:
    """Leases of one class still on the balance sheet at the reporting date."""
    return [
        s for s in register
        if s.lease_class == lease_class
        and s.remaining_liability > Decimal("0")   # exclude expired/terminated
        and s.remaining_term_months > 0
    ]

Step 3 — Accumulate the balance-weighted sums. Build the weighted numerator and the total-balance denominator in a single pass, keeping both in Decimal. The rate numerator multiplies each rate by its liability; the term numerator multiplies each term by the same liability, so both averages share one weight and one denominator.

def weighted_averages(register: list[LeaseSnapshot],
                      lease_class: str) -> dict:
    """Weighted-average discount rate and remaining term for one class.

    ASC 842-20-50-4(g)(3)-(4): both are weighted by the remaining lease
    liability balance and disclosed separately for finance and operating.
    """
    leases = active_leases(register, lease_class)
    total_balance = sum((s.remaining_liability for s in leases), Decimal("0"))
    if total_balance == Decimal("0"):
        return {"class": lease_class, "count": 0,
                "wavg_rate": None, "wavg_term_years": None}

    rate_numerator = sum(
        (s.discount_rate * s.remaining_liability for s in leases), Decimal("0"))
    term_numerator = sum(
        (Decimal(s.remaining_term_months) * s.remaining_liability
         for s in leases), Decimal("0"))

    wavg_rate = (rate_numerator / total_balance).quantize(
        Decimal("0.0001"), rounding=ROUND_HALF_UP)
    wavg_term_years = (term_numerator / total_balance / Decimal("12")).quantize(
        Decimal("0.1"), rounding=ROUND_HALF_UP)

    return {"class": lease_class, "count": len(leases),
            "wavg_rate": wavg_rate, "wavg_term_years": wavg_term_years}

Step 4 — Run per class, assert, and disclose. Compute each class independently — ASC 842 never blends finance and operating into one figure — and assert the reported rate falls between the smallest and largest per-lease rate, a cheap invariant that catches a broken weight or an inverted division.

if __name__ == "__main__":
    register = [
        LeaseSnapshot("L-01", "operating", Decimal("0.045"),
                      Decimal("1200000.00"), 84),
        LeaseSnapshot("L-02", "operating", Decimal("0.060"),
                      Decimal("800000.00"), 48),
        LeaseSnapshot("L-03", "operating", Decimal("0.075"),
                      Decimal("400000.00"), 24),
        LeaseSnapshot("L-99", "operating", Decimal("0.090"),
                      Decimal("0.00"), 0),          # terminated — must be excluded
    ]
    result = weighted_averages(register, "operating")
    rates = [s.discount_rate for s in active_leases(register, "operating")]
    assert min(rates) <= result["wavg_rate"] <= max(rates)   # bounded mean
    assert result["count"] == 3                              # expired lease dropped
    print(result)  # -> weighted rate ~0.0540, weighted term ~5.5 years

The assertion is the guardrail. A weighted average must always land between the minimum and maximum of the values being averaged; if it does not, the weight is negative, the numerator and denominator are mismatched, or a float has leaked in and corrupted the sum. Keeping the check in the disclosure pipeline runs it on every close, not just in an ad-hoc notebook.

Routing the Portfolio Into the Two Disclosures Link to this section

Before the arithmetic runs, the register has to be shaped into the right population and split along the one axis ASC 842 cares about — lease class. The diagram below traces that flow: every lease is tested for a live obligation, the survivors are partitioned into finance and operating, and each partition yields its own rate and term. Nothing crosses the class boundary.

Routing the lease register into the per-class weighted-average disclosures The full lease register enters a filter that keeps only leases with a remaining liability greater than zero at the reporting date, diverting fully amortized and terminated leases out of the population. The surviving leases split into a finance-lease branch and an operating-lease branch. Each branch computes its own balance-weighted discount rate and balance-weighted remaining term, producing two separate disclosure lines: weighted-average rate and term for finance leases, and weighted-average rate and term for operating leases. The two branches never blend. Lease register all leases, period end Remaining liability > 0? Excluded amortized / terminated Finance leases balance-weighted Operating leases balance-weighted Finance wavg rate r̄ wavg term T̄ Operating wavg rate r̄ wavg term T̄ no yes: finance yes: operating
The register is filtered to leases with a live obligation, then partitioned by class. Each class computes its own balance-weighted rate and term, yielding two independent disclosure lines; a fully amortized or terminated lease is diverted out of the population before any average is taken.

Debugging and Precision Gotchas Link to this section

The weighted-average step fails in a compact, well-known set of ways. Each has a deterministic fix.

  1. Equal-weighting instead of balance-weighting. Computing a plain arithmetic mean of the per-lease rates ignores that a USD 5 million liability and a USD 50,000 liability affect the reader's economics differently. ASC 842 intends a balance-weighted figure; the simple average can differ by a full percentage point on a skewed portfolio. Fix: always divide the liability-weighted numerator by the total liability, never by the lease count.
  2. Including expired or terminated leases. A lease that amortized to a zero balance, or was terminated mid-period, still sits in many registers with a stale rate and a zero or negative remaining term. Left in, it contributes a zero-weight rate (harmless) but a zero-term row that some naive term calculations average in by count, dragging the reported term down. Fix: filter on remaining_liability > 0 before aggregating, as in Step 2.
  3. Blending finance and operating classes. ASC 842-20-50-4(g) requires the two figures separately by class. Computing one portfolio-wide average understates or overstates each class and fails the disclosure requirement outright. Fix: partition first, aggregate second.
  4. Original term instead of remaining term. The disclosure is the weighted-average remaining lease term at the reporting date, not the original term at commencement. Using the commencement term overstates the figure every period after the first. Fix: recompute months-to-end from the reporting date each close.
  5. Mixing annual and periodic rates. The disclosed rate is the annual discount rate. If the register stores a monthly periodic rate for the amortization engine, converting inconsistently — or forgetting to convert — reports a rate an order of magnitude off. Fix: store and weight the annual rate; derive the periodic rate only inside the effective interest method.
  6. Float leakage in the sums. Accumulating the weighted numerator in float bakes binary-fraction error into a figure disclosed to four decimal places. Fix: keep both numerator and denominator in Decimal and quantize only the final ratio.

Compliance Checklist — Before You Publish the Weighted Averages Link to this section

Frequently Asked Questions Link to this section

What weight does ASC 842 use for the weighted-average discount rate?

The remaining balance of each lease liability. The reported rate is the sum of each lease's rate multiplied by its remaining liability, divided by the total remaining liability of the class. A simple arithmetic average of the rates is not compliant, because it gives a small lease the same influence as a large one; ASC 842-20-50-4(g)(4) intends a figure that reflects the financing cost of the obligation actually on the balance sheet.

How is the weighted-average remaining lease term weighted?

The Codification does not prescribe a single weight, but the dominant and most defensible practice weights the remaining term by the same remaining lease liability balance used for the rate, which keeps the two disclosures internally consistent. A supportable alternative weights the remaining term by the remaining undiscounted lease payments. Whichever you choose is an accounting policy that must be applied consistently across periods and lease classes.

Do IFRS 16 preparers have to disclose a weighted-average discount rate?

No. IFRS 16 has no line-item requirement equivalent to ASC 842-20-50-4(g); IFRS 16.53(g) is the total cash outflow for leases, not a rate. Under the disclosure objective in IFRS 16.51 and the additional-information provision in IFRS 16.59, many lessees voluntarily present a weighted-average incremental borrowing rate on the same balance-weighted basis, which lets a dual-reporting group reuse one calculation, but it is not mandated.

Should a fully amortized or terminated lease be included in the average?

No. A lease whose remaining liability has reached zero carries no obligation to weight and no remaining term to disclose, so it drops out of the population. Including it does not change a properly balance-weighted rate (its weight is zero) but it corrupts any term figure that is averaged by count and it inflates the disclosed population, so the cleaner control is to filter on a positive remaining liability before aggregating.

Up: Lease Disclosure and Financial Statement Reporting

Continue reading