Skip to content

Compound Interest Calculator — Zig source

Project investment growth with any compounding frequency, scheduled contributions and inflation adjustment. Yearly breakdown, growth curve and CSV export — runs entirely in your browser.

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

//! compound-interest-calculator — compound growth projection.
//!
//! Language: Zig (0.13, standard library only)
//! Source:   CosmoDev polyglot showcase port of the Compound Interest
//!           Calculator tool, ported from src/lib/interest.ts (the canonical
//!           TypeScript implementation).
//! Tool:     https://dev.cosmolabs.org/tools/compound-interest-calculator
//! License:  display source — part of CosmoDev's polyglot tool pages.
//!
//! Display snippet: ports the core `project` math from the TypeScript lib.
//! The full projection (contributions, inflation, per-year rows) lives in
//! the TypeScript/Go sources.
//!
//! Semantics: monthly simulation with the monthly rate set to the exact
//! equivalent of the stated compounding frequency — (1 + r/n)^(n/12) - 1 —
//! so the effective annual rate is preserved for every supported
//! frequency. Returns the final balance rounded to 2 decimals, or `null`
//! on invalid input (negative principal, negative years, unsupported
//! frequency, 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;
}

pub fn project(principal: f64, rate_pct: f64, years: i64, compounds_per_year: u32) ?f64 {
    const frequency_ok = switch (compounds_per_year) {
        1, 2, 4, 12, 365 => true,
        else => false,
    };
    if (!std.math.isFinite(principal) or !std.math.isFinite(rate_pct) or
        principal < 0 or years < 0 or !frequency_ok)
    {
        return null;
    }

    const n: f64 = @floatFromInt(compounds_per_year);
    const monthly_rate = std.math.pow(f64, 1.0 + rate_pct / 100.0 / n, n / 12.0) - 1.0;

    var balance = principal;
    var i: i64 = 0;
    while (i < years * 12) : (i += 1) {
        balance *= 1.0 + monthly_rate;
    }
    return round2(balance);
}

pub fn main() void {
    // Example:
    //   project(10000.0, 5.0, 10, 12) -> 16470.09
    const result = project(10000.0, 5.0, 10, 12).?;
    std.debug.assert(@abs(result - 16470.09) < 0.005);

    // Invalid inputs are rejected, never clamped.
    std.debug.assert(project(-1.0, 5.0, 10, 12) == null); // negative principal
    std.debug.assert(project(10000.0, 5.0, -1, 12) == null); // negative years
    std.debug.assert(project(10000.0, 5.0, 10, 6) == null); // unsupported frequency
    std.debug.assert(project(std.math.inf(f64), 5.0, 10, 12) == 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 →