Skip to content

Mortgage Calculator — C# source

Compute monthly payments, the full amortization schedule and total interest, with extra-payment what-if analysis showing months and interest saved — runs entirely in your browser.

This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.

// mortgage-calculator — amortized monthly payment.
//
// Language: C# (.NET 8, standard library only)
// Source:   CosmoDev polyglot showcase port of the Mortgage Calculator
//           tool, ported from src/lib/mortgage.ts (the canonical
//           TypeScript implementation).
// Tool:     https://dev.cosmolabs.org/tools/mortgage-calculator
// License:  display source — part of CosmoDev's polyglot tool pages.
//
// Display snippet: ports the core MonthlyPayment() formula from the
// TypeScript lib. The full amortization schedule (integer-cent tracking,
// extra payments, payoff dates) lives in the TypeScript/Go sources.
//
// Semantics: standard amortization formula P*r*(1+r)^n / ((1+r)^n - 1)
// with r = ratePct/100/12 and n = years*12. A zero rate divides the
// principal evenly across n months. Returns the payment rounded to
// cents, or null on invalid input (non-positive principal, non-positive
// years, non-finite values).

using System;

namespace MortgageCalculatorTool
{
    public static class Mortgage
    {
        // Round to 2 decimals, half-up (mirrors the TS Math.round).
        private static double Round2(double n) => Math.Floor(n * 100.0 + 0.5) / 100.0;

        public static double? MonthlyPayment(double principal, double ratePct, int years)
        {
            if (!double.IsFinite(principal) || !double.IsFinite(ratePct))
                return null;
            if (principal <= 0.0 || years <= 0)
                return null;

            double r = ratePct / 100.0 / 12.0;
            double n = years * 12.0;

            double payment;
            if (r == 0.0)
            {
                payment = principal / n;
            }
            else
            {
                double growth = Math.Pow(1.0 + r, n);
                payment = principal * r * growth / (growth - 1.0);
            }
            return Round2(payment);
        }

        public static void Main()
        {
            // Example:
            //   MonthlyPayment(300000, 6, 30) -> 1798.65
            double? result = MonthlyPayment(300000.0, 6.0, 30);
            if (result is null || result != 1798.65)
                throw new InvalidOperationException($"expected 1798.65, got {result}");

            // A zero rate divides the principal evenly.
            if (MonthlyPayment(300000.0, 0.0, 30) != 833.33)
                throw new InvalidOperationException("expected 833.33 at a zero rate");

            // Invalid inputs are rejected, never clamped.
            if (MonthlyPayment(-1.0, 6.0, 30) is not null) throw new InvalidOperationException("non-positive principal");
            if (MonthlyPayment(300000.0, 6.0, 0) is not null) throw new InvalidOperationException("non-positive years");
            if (MonthlyPayment(double.PositiveInfinity, 6.0, 30) is not null) throw new InvalidOperationException("non-finite value");

            Console.WriteLine("ok");
        }
    }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →