Percentage Calculator — PHP 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 PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.
<?php
/*
* percentage-calculator — PHP 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.
*/
namespace CosmoDev\Percentage;
const DEFAULT_MAX_DECIMALS = 2;
/**
* Options governing result precision. `maxDecimals` defaults to 2 when null,
* mirroring the TypeScript PercentOptions optional field — which (unlike an
* int) distinguishes "unset" from an explicit 0.
*/
final class PercentOptions
{
public function __construct(
public readonly ?int $maxDecimals = null,
) {}
}
/**
* Reports whether every value in $vals is finite. NAN and ±INF are treated as
* invalid inputs throughout this namespace: callers receive null rather than a
* propagated NAN.
*/
function every_finite(float ...$vals): bool
{
foreach ($vals as $v) {
if (!is_finite($v)) {
return false;
}
}
return true;
}
/** Resolve the effective precision, falling back to the default when unset. */
function resolve_decimals(PercentOptions $opts): int
{
return $opts->maxDecimals ?? DEFAULT_MAX_DECIMALS;
}
/**
* Round $n to at most $maxDecimals places.
*
* Adding PHP_FLOAT_EPSILON before scaling absorbs the tiny errors that arise
* from representing decimal fractions in binary floating point (the classic
* `0.1 + 0.2 != 0.3` problem); it also nudges exact `.5` ties onto a side that
* agrees with JavaScript's Math.round, keeping this port byte-for-byte
* consistent with the TypeScript source. Non-finite values are returned
* unchanged so this function is total.
*
* Named `round_value` to avoid shadowing PHP's built-in `round()`, which is
* used internally to do the scaled rounding.
*/
function round_value(float $n, int $maxDecimals): float
{
if (!is_finite($n)) {
return $n;
}
$factor = 10 ** $maxDecimals;
return round(($n + PHP_FLOAT_EPSILON) * $factor) / $factor;
}
/**
* X% of $value: ($pct / 100) * $value. Returns null when either input is
* non-finite.
*/
function percent_of(float $pct, float $value, ?PercentOptions $opts = null): ?float
{
$opts ??= new PercentOptions();
if (!every_finite($pct, $value)) {
return null;
}
return round_value(($pct / 100) * $value, resolve_decimals($opts));
}
/**
* 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.
*/
function what_percent(float $part, float $total, ?PercentOptions $opts = null): ?float
{
$opts ??= new PercentOptions();
if (!every_finite($part, $total)) {
return null;
}
if ($total == 0.0) {
return null;
}
return round_value(($part / $total) * 100, resolve_decimals($opts));
}
/**
* Percentage change from $from to $to: (($to - $from) / abs($from)) * 100.
*
* The denominator is absolute so the result's sign reflects only the
* direction of change (positive for an increase, negative for a decrease).
* Returns null when $from is zero (no meaningful base to compare against) or
* either input is non-finite.
*/
function percent_change(float $from, float $to, ?PercentOptions $opts = null): ?float
{
$opts ??= new PercentOptions();
if (!every_finite($from, $to)) {
return null;
}
if ($from == 0.0) {
return null;
}
return round_value((($to - $from) / abs($from)) * 100, resolve_decimals($opts));
}
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 →