Skip to content

Percentage Calculator — Java source

Calculate percentages three ways - X% of Y, X is what percent of Y, and the percentage change between two values. Runs entirely in your browser, with a shareable link.

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

// percentage-calculator — Java port: three-way percentage math (X% of Y, X as % of Y, % change).

/** Pure percentage logic, ported from the canonical TS lib. Deterministic and
 * side-effect free: every function returns null for non-finite input or an
 * undefined result (a zero divisor) instead of throwing, and rounds to a
 * configurable maximum number of decimals.
 *
 * Package-private because the display file is named java.java, not
 * Percentage.java — the only way a public top-level class may sit in a
 * mismatched filename is to drop the modifier. */
final class Percentage {

    /** Rounding precision when an option is omitted, matching
     * DEFAULT_MAX_DECIMALS in src/lib/percentage.ts. */
    public static final int DEFAULT_MAX_DECIMALS = 2;

    /** 2^-52 — identical to JavaScript's Number.EPSILON (Math.ulp(1.0)). */
    private static final double JS_EPSILON = Math.ulp(1.0);

    /** Options governing result precision. maxDecimals == null means
     * "omitted" → the default, keeping an explicit 0 distinct from an unset
     * value (the TS optional-field semantics). */
    public record PercentOptions(Integer maxDecimals) {

        /** The effective precision: the explicit option when set, otherwise
         * the default. Mirrors the TS default-parameter behavior of round(). */
        int resolve() {
            return maxDecimals != null ? maxDecimals : DEFAULT_MAX_DECIMALS;
        }
    }

    /** Round to at most maxDecimals places, absorbing binary float noise via
     * the epsilon nudge (the classic 0.005 → 0.01 case). Non-finite values
     * pass through unchanged so this function is total, exactly like the TS
     * round(). */
    public static double roundValue(double n, int maxDecimals) {
        if (!Double.isFinite(n)) return n;
        double factor = Math.pow(10, maxDecimals);
        // Math.floor(x + 0.5) replicates JavaScript's Math.round (half toward
        // +infinity). Math.round(long) is equivalent in effect but returns a
        // long, losing the symmetric pass-through shape of the TS source.
        return Math.floor((n + JS_EPSILON) * factor + 0.5) / factor;
    }

    /** X% of value: pct / 100 * value. null (the Java analogue of the TS
     * null) when either input is non-finite. */
    public static Double percentOf(double pct, double value, PercentOptions opts) {
        if (!Double.isFinite(pct) || !Double.isFinite(value)) return null;
        return roundValue((pct / 100) * value, opts.resolve());
    }

    /** What percentage part is of total: part / total * 100. null when total
     * is 0 (undefined ratio) or either input is non-finite. */
    public static Double whatPercent(double part, double total, PercentOptions opts) {
        if (!Double.isFinite(part) || !Double.isFinite(total) || total == 0) return null;
        return roundValue((part / total) * 100, opts.resolve());
    }

    /** Percentage change from from to to, measured against |from| so the
     * result's sign reflects only the direction of change. null when from is
     * 0 (no meaningful base) or either input is non-finite. */
    public static Double percentChange(double from, double to, PercentOptions opts) {
        if (!Double.isFinite(from) || !Double.isFinite(to) || from == 0) return null;
        return roundValue(((to - from) / Math.abs(from)) * 100, opts.resolve());
    }

    public static void main(String[] args) {
        PercentOptions omitted = new PercentOptions(null); // default → 2 decimals
        PercentOptions zero = new PercentOptions(0);       // explicit 0 ≠ omitted

        show("15% of 200", percentOf(15, 200, omitted));
        show("30 is what % of 200", whatPercent(30, 200, omitted));
        show("change 40 -> 50", percentChange(40, 50, omitted));
        show("change 50 -> 40", percentChange(50, 40, omitted));
        show("15% of 37, 0 decimals", percentOf(15, 37, zero));
        show("30 is what % of 0", whatPercent(30, 0, omitted));
        show("change 0 -> 50", percentChange(0, 50, omitted));
    }

    /** Demo helper: print "null" for the undefined result, integers without a
     * trailing ".0" (via the double formatter's minimal representation). */
    private static void show(String label, Double v) {
        String text = v == null ? "null" : String.valueOf(v);
        System.out.printf("%-24s = %s%n", label, text);
    }
}

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 →