Percentage Calculator — JavaScript 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 JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
/**
* percentage-calculator - JavaScript port.
*
* CosmoDev polyglot showcase port of the `percentage-calculator` tool. Pure
* percentage logic ported from src/lib/percentage.ts. 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 decimal places.
*
* Display source - part of CosmoDev's polyglot tool pages.
*/
/**
* @typedef {Object} PercentOptions
* @property {number} [maxDecimals] - Maximum decimal places in the result
* (defaults to 2 when omitted, mirroring the TypeScript optional field).
*/
const DEFAULT_MAX_DECIMALS = 2;
/**
* Reports whether every value in `vals` is a finite number. NaN and ±Infinity
* are treated as invalid inputs throughout this module (they propagate nowhere
* - callers receive null instead of a garbage result).
*
* @param {...number} vals
* @returns {boolean}
*/
function everyFinite(...vals) {
return vals.every(Number.isFinite);
}
/**
* Round to at most `maxDecimals` places (default 2).
*
* Adding Number.EPSILON before scaling absorbs the tiny errors that arise from
* representing decimal fractions in binary floating point - the classic
* `0.1 + 0.2 === 0.30000000000000004`. Non-finite values are returned
* unchanged so this helper is total and safe to use before a null check.
*
* @param {number} n
* @param {number} [maxDecimals=DEFAULT_MAX_DECIMALS]
* @returns {number}
*/
export function round(n, maxDecimals = DEFAULT_MAX_DECIMALS) {
if (!Number.isFinite(n)) return n;
const factor = 10 ** maxDecimals;
return Math.round((n + Number.EPSILON) * factor) / factor;
}
/**
* X% of `value`: `(pct / 100) * value`.
*
* Returns null when either input is non-finite.
*
* @param {number} pct
* @param {number} value
* @param {PercentOptions} [opts={}]
* @returns {number | null}
*/
export function percentOf(pct, value, opts = {}) {
if (!everyFinite(pct, value)) return null;
return round((pct / 100) * value, opts.maxDecimals);
}
/**
* What percentage `part` is of `total`: `(part / total) * 100`.
*
* Returns null when `total` is zero (the ratio is undefined) or either input
* is non-finite.
*
* @param {number} part
* @param {number} total
* @param {PercentOptions} [opts={}]
* @returns {number | null}
*/
export function whatPercent(part, total, opts = {}) {
if (!everyFinite(part, total)) return null;
if (total === 0) return null;
return round((part / total) * 100, opts.maxDecimals);
}
/**
* Percentage change going from `from` to `to`: `((to - from) / |from|) * 100`.
*
* The denominator is absolute so the result's sign reflects only the
* direction of change (positive for increase, negative for decrease).
* Returns null when `from` is zero (no meaningful base to compare against) or
* either input is non-finite.
*
* @param {number} from
* @param {number} to
* @param {PercentOptions} [opts={}]
* @returns {number | null}
*/
export function percentChange(from, to, opts = {}) {
if (!everyFinite(from, to)) return null;
if (from === 0) return null;
return round(((to - from) / Math.abs(from)) * 100, opts.maxDecimals);
}
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 →