Skip to content

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

// compound-interest-calculator — compound growth projection.
//
// Language: C# (C# 12 / .NET 8, 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).

using System;
using System.Collections.Generic;

namespace CompoundInterestCalculator;

public static class Interest
{
    private static readonly HashSet<int> ValidCompounds = new() { 1, 2, 4, 12, 365 };

    // 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? Project(double principal, double ratePct, int years, int compoundsPerYear)
    {
        if (!double.IsFinite(principal) || !double.IsFinite(ratePct))
        {
            return null;
        }
        if (principal < 0.0 || years < 0 || !ValidCompounds.Contains(compoundsPerYear))
        {
            return null;
        }

        double n = compoundsPerYear;
        double monthlyRate = Math.Pow(1.0 + ratePct / 100.0 / n, n / 12.0) - 1.0;

        double balance = principal;
        for (int i = 0; i < years * 12; i++)
        {
            balance *= 1.0 + monthlyRate;
        }
        return Round2(balance);
    }
}

public static class Program
{
    public static void Main()
    {
        // Example:
        //   Project(10000, 5, 10, 12) -> 16470.09
        var result = Interest.Project(10000.0, 5.0, 10, 12);
        if (result is not 16470.09)
        {
            throw new InvalidOperationException($"expected 16470.09, got {result}");
        }

        // Invalid inputs are rejected, never clamped.
        if (Interest.Project(-1.0, 5.0, 10, 12) is not null) throw new InvalidOperationException("negative principal");
        if (Interest.Project(10000.0, 5.0, -1, 12) is not null) throw new InvalidOperationException("negative years");
        if (Interest.Project(10000.0, 5.0, 10, 6) is not null) throw new InvalidOperationException("unsupported frequency");
        if (Interest.Project(double.PositiveInfinity, 5.0, 10, 12) 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 →