Skip to content

Mortgage Calculator — Zig 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 Zig implementation — the same logic the interactive tool runs, in a shareable, citable form.

// mortgage-calculator — amortized monthly payment.
//
// Language: Zig (0.11, 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 = rate_pct/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).

const std = @import("std");

/// Round to 2 decimals, half-up (mirrors the TS Math.round).
fn round2(n: f64) f64 {
    return @floor(n * 100.0 + 0.5) / 100.0;
}

/// The amortized monthly payment, or null on invalid input.
pub fn monthlyPayment(principal: f64, rate_pct: f64, years: i32) ?f64 {
    if (!std.math.isFinite(principal) or !std.math.isFinite(rate_pct))
        return null;
    if (principal <= 0.0 or years <= 0)
        return null;

    const r = rate_pct / 100.0 / 12.0;
    const n: f64 = @floatFromInt(years * 12);

    const payment = if (r == 0.0)
        principal / n
    else blk: {
        const growth = std.math.pow(f64, 1.0 + r, n);
        break :blk principal * r * growth / (growth - 1.0);
    };
    return round2(payment);
}

pub fn main() void {
    // Example:
    //   monthlyPayment(300000.0, 6.0, 30) -> 1798.65
    const result = monthlyPayment(300000.0, 6.0, 30).?;
    std.debug.assert(@abs(result - 1798.65) < 0.005);

    // A zero rate divides the principal evenly.
    const zero_rate = monthlyPayment(300000.0, 0.0, 30).?;
    std.debug.assert(@abs(zero_rate - 833.33) < 0.005);

    // Invalid inputs are rejected, never clamped.
    std.debug.assert(monthlyPayment(-1.0, 6.0, 30) == null); // non-positive principal
    std.debug.assert(monthlyPayment(300000.0, 6.0, 0) == null); // non-positive years
    std.debug.assert(monthlyPayment(std.math.inf(f64), 6.0, 30) == null); // non-finite value

    std.debug.print("ok\n", .{});
}

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 →