Skip to content

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

// =============================================================================
//  DpiPpi.swift — CosmoDev polyglot showcase port of the `dpi-ppi` tool
//  -----------------------------------------------------------------------------
//  Language : Swift (5.9, 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, 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 safeDiv wrapper
//  only because Python raises ZeroDivisionError for float division.)
// =============================================================================

/// Display pixel-density math.
public enum DpiPpi {

    /// Fixed conversion factor between inches and millimeters.
    private static let mmPerInch: Double = 25.4

    /// Pixels-per-inch result.
    public struct Ppi: Equatable {
        public let ppi: Double

        public init(ppi: Double) { self.ppi = ppi }
    }

    /// Physical dimensions of a display, in inches and millimeters.
    public struct PhysicalSize: Equatable {
        public let widthIn: Double
        public let heightIn: Double
        public let diagonalIn: Double
        public let widthMm: Double
        public let heightMm: Double

        public init(
            widthIn: Double,
            heightIn: Double,
            diagonalIn: Double,
            widthMm: Double,
            heightMm: Double
        ) {
            self.widthIn = widthIn
            self.heightIn = heightIn
            self.diagonalIn = diagonalIn
            self.widthMm = widthMm
            self.heightMm = heightMm
        }
    }

    /// Diagonal length in pixels: √(w² + h²).
    /// The sign of the inputs is irrelevant because both terms are squared.
    public static func diagonalPixels(_ w: Double, _ h: Double) -> Double {
        (w * w + h * h).squareRoot()
    }

    /// Pixels per inch, given a resolution and a known physical diagonal in inches.
    public static func computePpi(_ w: Double, _ h: Double, diagonalInches: Double) -> Ppi {
        Ppi(ppi: diagonalPixels(w, h) / diagonalInches)
    }

    /// 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.
    public static func physicalSize(_ w: Double, _ h: Double, ppi: Double) -> PhysicalSize {
        let widthIn = w / ppi
        let heightIn = h / ppi
        return PhysicalSize(
            widthIn: widthIn,
            heightIn: heightIn,
            diagonalIn: diagonalPixels(widthIn, heightIn),
            widthMm: widthIn * mmPerInch,
            heightMm: heightIn * mmPerInch)
    }

    /// Dot pitch (pixel size) in millimeters per pixel: 25.4 / ppi.
    public static func dotPitch(_ ppi: Double) -> Double {
        mmPerInch / ppi
    }

    /// Saturating Double → Int64 truncation, mirroring Rust's `as i64` cast
    /// semantics (NaN → 0, ±out-of-range → min/max). Avoids the runtime trap
    /// of `Int64(x)` on pathological ±inf inputs; real display sizes are
    /// finite.
    private static func truncToI64(_ x: Double) -> Int64 {
        let t = x.rounded(.towardZero)
        if t.isNaN { return 0 }
        if t >= 9_223_372_036_854_775_808.0 { return Int64.max }  // 2^63
        if t < -9_223_372_036_854_775_808.0 { return Int64.min }
        return Int64(t)
    }

    /// 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.
    private static func gcd(_ a: Double, _ b: Double) -> Int64 {
        var x = truncToI64(a.magnitude)
        var y = truncToI64(b.magnitude)
        while y != 0 {
            (x, y) = (y, x % y)
        }
        return x
    }

    /// 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).
    public static func aspectRatio(_ w: Double, _ h: Double) -> String {
        if !(w > 0) || !(h > 0) {
            return "-"
        }
        let g = gcd(w, h)
        if g == 0 {
            return "-"
        }
        return "\(truncToI64(w) / g):\(truncToI64(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 →