Percentage Calculator — C 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 C implementation — the same logic the interactive tool runs, in a shareable, citable form.
/* percentage-calculator — C port: three-way percentage math (X% of Y, X as % of Y, % change). */
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
/* Rounding precision when an option is omitted, matching
* DEFAULT_MAX_DECIMALS in src/lib/percentage.ts. */
#define DEFAULT_MAX_DECIMALS 2
/* 2^-52 — identical to JavaScript's Number.EPSILON (C's DBL_EPSILON is the
* same value; spelled out so the port reads directly against the TS source). */
#define JS_EPSILON 2.2204460492503131e-16
/* Options governing result precision. has_max_decimals == false means
* "omitted" → DEFAULT_MAX_DECIMALS. The flag keeps an explicit 0 distinct
* from an unset value, mirroring the TS optional field. */
typedef struct {
bool has_max_decimals;
int max_decimals;
} percent_options;
/* Effective precision: the explicit option when set, otherwise the default.
* Mirrors the TS default-parameter behavior of round(). */
static int resolve_max_decimals(const percent_options *opts)
{
return opts->has_max_decimals ? opts->max_decimals : DEFAULT_MAX_DECIMALS;
}
/* Both values finite (not NaN, not ±inf)? The TS everyFinite(...vals) twin —
* every public function below takes exactly two numbers, so two slots suffice. */
static bool both_finite(double a, double b)
{
return isfinite(a) && isfinite(b);
}
/* JavaScript's Math.round: round half toward +infinity — floor(x + 0.5) —
* NOT C's round(), which sends .5 away from zero. floor(x + 0.5) matches the
* TS source on negative .5 ties (Math.round(-2.5) === -2). */
static double js_round(double x)
{
return floor(x + 0.5);
}
/* Round to at most max_decimals 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(). */
static double round_value(double n, int max_decimals)
{
if (!isfinite(n))
return n;
double factor = pow(10, max_decimals);
return js_round((n + JS_EPSILON) * factor) / factor;
}
/* X% of value: pct / 100 * value. NAN — the C analogue of the TS null — when
* either input is non-finite. */
double percent_of(double pct, double value, const percent_options *opts)
{
if (!both_finite(pct, value))
return NAN;
return round_value((pct / 100) * value, resolve_max_decimals(opts));
}
/* What percentage part is of total: part / total * 100. NAN when total is 0
* (undefined ratio) or either input is non-finite. */
double what_percent(double part, double total, const percent_options *opts)
{
if (!both_finite(part, total) || total == 0)
return NAN;
return round_value((part / total) * 100, resolve_max_decimals(opts));
}
/* Percentage change from `from` to `to`, measured against |from| so the
* result's sign reflects only the direction of change. NAN when `from` is 0
* (no meaningful base) or either input is non-finite. */
double percent_change(double from, double to, const percent_options *opts)
{
if (!both_finite(from, to) || from == 0)
return NAN;
return round_value(((to - from) / fabs(from)) * 100, resolve_max_decimals(opts));
}
/* Demo helper: print "null" for the undefined result, integers without a
* trailing ".0", and everything else with %g. */
static void show(const char *label, double v)
{
if (isnan(v))
printf("%-24s = null\n", label);
else if (v == (long long)v)
printf("%-24s = %lld\n", label, (long long)v);
else
printf("%-24s = %g\n", label, v);
}
int main(void)
{
const percent_options omitted = { false, 0 }; /* default → 2 decimals */
const percent_options zero = { true, 0 }; /* explicit 0 ≠ omitted */
show("15% of 200", percent_of(15, 200, &omitted));
show("30 is what % of 200", what_percent(30, 200, &omitted));
show("change 40 -> 50", percent_change(40, 50, &omitted));
show("change 50 -> 40", percent_change(50, 40, &omitted));
show("15% of 37, 0 decimals", percent_of(15, 37, &zero));
show("30 is what % of 0", what_percent(30, 0, &omitted));
show("change 0 -> 50", percent_change(0, 50, &omitted));
return 0;
}
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 →