Skip to content

DPI / PPI Calculator — Python 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 Python implementation — the same logic the interactive tool runs, in a shareable, citable form.

"""Display pixel-density math (Python port).

Pure, deterministic, never raises. Non-positive inputs that would cause
division by zero produce NaN or Infinity (mirroring JavaScript's IEEE-754
behavior) rather than raising — callers should guard with math.isfinite
before display.

Note: Python raises ZeroDivisionError on a float ``/0``. To stay output-
equivalent with the JavaScript reference (which yields inf/nan), every
division in this module is routed through ``safe_div``.

CosmoDev polyglot showcase port of ``dpi-ppi``, ported from
src/lib/dpi-ppi.ts. Display source — part of CosmoDev's polyglot tool pages.
"""

from __future__ import annotations

import math
from typing import TypedDict

# Fixed conversion factor between inches and millimeters.
MM_PER_INCH: float = 25.4


class PpiResult(TypedDict):
    """Pixels-per-inch result."""

    ppi: float


class PhysicalSize(TypedDict):
    """Physical dimensions of a display, in inches and millimeters."""

    width_in: float
    height_in: float
    diagonal_in: float
    width_mm: float
    height_mm: float


def safe_div(n: float, d: float) -> float:
    """IEEE-754-style float division.

    Returns +/-inf (or nan for the 0/0 case) when the divisor is zero instead
    of letting Python raise ZeroDivisionError, keeping results equivalent with
    the JavaScript reference implementation.
    """
    if d == 0.0:
        if n == 0.0:
            return math.nan
        return math.inf if n > 0.0 else -math.inf
    return n / d


def diagonal_pixels(w: float, h: float) -> float:
    """Diagonal length in pixels: √(w² + h²).

    The sign of the inputs is irrelevant because both terms are squared.
    """
    return math.sqrt(w * w + h * h)


def compute_ppi(w: float, h: float, diagonal_inches: float) -> PpiResult:
    """Pixels per inch, given a resolution and a known physical diagonal."""
    return {"ppi": safe_div(diagonal_pixels(w, h), diagonal_inches)}


def physical_size(w: float, h: float, ppi: float) -> PhysicalSize:
    """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.
    """
    width_in = safe_div(w, ppi)
    height_in = safe_div(h, ppi)
    return {
        "width_in": width_in,
        "height_in": height_in,
        "diagonal_in": diagonal_pixels(width_in, height_in),
        "width_mm": width_in * MM_PER_INCH,
        "height_mm": height_in * MM_PER_INCH,
    }


def dot_pitch(ppi: float) -> float:
    """Dot pitch (pixel size) in millimeters per pixel: 25.4 / ppi."""
    return safe_div(MM_PER_INCH, ppi)


def _gcd(a: float, b: float) -> int:
    """Integer gcd 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.
    """
    x = abs(math.trunc(a))
    y = abs(math.trunc(b))
    while y != 0:
        x, y = y, x % y
    return x


def aspect_ratio(w: float, h: float) -> str:
    """Reduced aspect ratio as ``"w:h"`` (e.g. ``"16:9"``).

    Returns ``"-"`` when either side is not a positive number (this also
    covers NaN, since every comparison with NaN is false).
    """
    if not (w > 0) or not (h > 0):
        return "-"
    g = _gcd(w, h)
    if g == 0:
        return "-"
    # Exact integer division: g divides each side evenly, so // yields the
    # clean ratio component with no decimal point.
    return f"{math.trunc(w) // g}:{math.trunc(h) // g}"

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 →