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 <cmath>
#include <iostream>
#include <optional>
#include <string>
// Rounding precision when an option is omitted, matching
// DEFAULT_MAX_DECIMALS in src/lib/percentage.ts.
inline constexpr int kDefaultMaxDecimals = 2;
// 2^-52 — identical to JavaScript's Number.EPSILON
// (std::numeric_limits<double>::epsilon() is the same value).
inline constexpr double kJsEpsilon = 2.2204460492503131e-16;
// Options governing result precision. nullopt means "omitted" → the default,
// keeping an explicit 0 distinct from an unset value (the TS optional field).
struct PercentOptions {
std::optional<int> max_decimals;
};
// Effective precision: the explicit option when set, otherwise the default.
// Mirrors the TS default-parameter behavior of round().
inline int resolve_max_decimals(const PercentOptions &opts)
{
return opts.max_decimals.value_or(kDefaultMaxDecimals);
}
// Every value finite (not NaN, not ±inf)? The TS everyFinite(...vals) twin,
// as a variadic fold so call sites read like the TS spread.
template <typename... Vals>
bool every_finite(Vals... vals)
{
return (... && std::isfinite(vals));
}
// JavaScript's Math.round: round half toward +infinity — floor(x + 0.5) —
// NOT std::round, which sends .5 away from zero. floor(x + 0.5) matches the
// TS source on negative .5 ties (Math.round(-2.5) === -2).
inline double js_round(double x)
{
return std::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().
double round_value(double n, int max_decimals = kDefaultMaxDecimals)
{
if (!std::isfinite(n))
return n;
const double factor = std::pow(10, max_decimals);
return js_round((n + kJsEpsilon) * factor) / factor;
}
// X% of value: pct / 100 * value. nullopt (the C++ analogue of the TS null)
// when either input is non-finite.
std::optional<double> percent_of(double pct, double value, const PercentOptions &opts = {})
{
if (!every_finite(pct, value))
return std::nullopt;
return round_value((pct / 100) * value, resolve_max_decimals(opts));
}
// What percentage part is of total: part / total * 100. nullopt when total
// is 0 (undefined ratio) or either input is non-finite.
std::optional<double> what_percent(double part, double total, const PercentOptions &opts = {})
{
if (!every_finite(part, total) || total == 0)
return std::nullopt;
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. nullopt when `from`
// is 0 (no meaningful base) or either input is non-finite.
std::optional<double> percent_change(double from, double to, const PercentOptions &opts = {})
{
if (!every_finite(from, to) || from == 0)
return std::nullopt;
return round_value(((to - from) / std::abs(from)) * 100, resolve_max_decimals(opts));
}
// Demo helper: print "null" for the undefined result, integers without a
// trailing ".0", and everything else with %g.
void show(const std::string &label, const std::optional<double> &v)
{
if (!v)
std::cout << label << " = null\n";
else if (*v == static_cast<long long>(*v))
std::cout << label << " = " << static_cast<long long>(*v) << "\n";
else
std::cout << label << " = " << *v << "\n";
}
int main()
{
const PercentOptions omitted; // default → 2 decimals
const PercentOptions zero{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 →