Skip to content

DPI / PPI Calculator — C source

Compute screen pixel density (PPI/DPI), dot pitch, physical dimensions, and aspect ratio from any resolution. Solve for PPI from a diagonal, or derive the diagonal from a known PPI - all in your browser.

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

/* =============================================================================
 *  dpi-ppi.c — CosmoDev polyglot showcase port of the `dpi-ppi` tool
 *  -----------------------------------------------------------------------------
 *  Language : C (C11, standard library only)
 *  Source:   ported from src/lib/dpi-ppi.ts (the canonical, live TypeScript
 *             lib); mirrors src/tool-sources/dpi-ppi/{python.py,rust.rs}
 *  License  : display source — part of CosmoDev's polyglot tool pages
 *             (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/
 *             Python ports and the other language ports.
 *  -----------------------------------------------------------------------------
 *  Display pixel-density math. Diagonal length in pixels, pixels-per-inch
 *  computed from a known physical diagonal, physical width/height/diagonal in
 *  both inches and millimeters, dot pitch in millimeters per pixel, and a
 *  reduced aspect ratio — all derived from a pixel resolution.
 *
 *  Pure, deterministic, side-effect free. A zero divisor does not trap:
 *  double division follows IEEE-754 wherever C11 Annex F (IEC 60559) is
 *  defined — mainstream compilers default to it — so x / 0.0 yields
 *  ±infinity and 0.0 / 0.0 yields NaN. Callers should guard with isfinite()
 *  before display. (The Python port routes every division through a safe_div
 *  wrapper only because Python raises ZeroDivisionError for float division.)
 * =============================================================================
 */

#include <math.h>
#include <stdio.h>

/* Fixed conversion factor between inches and millimeters. */
static const double MM_PER_INCH = 25.4;

/* Pixels-per-inch result. */
typedef struct {
    double ppi;
} dpi_ppi_ppi;

/* Physical dimensions of a display, in inches and millimeters. */
typedef struct {
    double width_in;
    double height_in;
    double diagonal_in;
    double width_mm;
    double height_mm;
} dpi_ppi_physical_size;

/* Diagonal length in pixels: sqrt(w^2 + h^2).
 * The sign of the inputs is irrelevant because both terms are squared. */
double dpi_ppi_diagonal_pixels(double w, double h)
{
    return sqrt(w * w + h * h);
}

/* Pixels per inch, given a resolution and a known physical diagonal in inches. */
dpi_ppi_ppi dpi_ppi_compute_ppi(double w, double h, double diagonal_inches)
{
    dpi_ppi_ppi r;
    r.ppi = dpi_ppi_diagonal_pixels(w, h) / diagonal_inches;
    return r;
}

/* Derive inches and millimeters from a resolution and a ppi. The diagonal is
 * recomputed from the derived inch dimensions so it stays consistent with the
 * width/height values. */
dpi_ppi_physical_size dpi_ppi_physical_size_from_ppi(double w, double h, double ppi)
{
    dpi_ppi_physical_size s;
    s.width_in = w / ppi;
    s.height_in = h / ppi;
    s.diagonal_in = dpi_ppi_diagonal_pixels(s.width_in, s.height_in);
    s.width_mm = s.width_in * MM_PER_INCH;
    s.height_mm = s.height_in * MM_PER_INCH;
    return s;
}

/* Dot pitch (pixel size) in millimeters per pixel: 25.4 / ppi. */
double dpi_ppi_dot_pitch(double ppi)
{
    return MM_PER_INCH / ppi;
}

/* Integer greatest-common-divisor via Euclid's algorithm, used to reduce
 * aspect ratios. Inputs are truncated toward zero and taken in absolute
 * value first, so the sign and fractional part of the inputs never matter.
 * (The double-to-long long casts assume finite inputs — the w > 0 / h > 0
 * guard in dpi_ppi_aspect_ratio has already rejected NaN and negatives, and
 * real display resolutions are far below the i64 range.) */
static long long gcd_ll(double a, double b)
{
    long long x = (long long)fabs(trunc(a));
    long long y = (long long)fabs(trunc(b));
    while (y != 0) {
        long long t = y;
        y = x % y;
        x = t;
    }
    return x;
}

/* Reduced aspect ratio as "w:h" (e.g. "16:9"), written into `buf` — a buffer
 * the caller supplies and owns; the return value is also `buf`, for chaining.
 * Writes "-" when either side is not a positive number (this also covers NaN,
 * since every comparison with NaN is false) or when the ratio will not
 * reduce. No heap is involved; 32 bytes is ample for any i64 pair. */
char *dpi_ppi_aspect_ratio(double w, double h, char *buf, size_t bufsz)
{
    if (!(w > 0.0) || !(h > 0.0)) {
        snprintf(buf, bufsz, "-");
        return buf;
    }
    long long g = gcd_ll(w, h);
    if (g == 0) {
        snprintf(buf, bufsz, "-");
        return buf;
    }
    long long wi = (long long)trunc(w);
    long long hi = (long long)trunc(h);
    snprintf(buf, bufsz, "%lld:%lld", wi / g, hi / g);
    return buf;
}

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 →