Skip to content

Data Unit Converter — Ruby source

Convert between digital data units - B, KB/KiB, MB/MiB, GB/GiB, TB/TiB. Switch between decimal (1000) and binary (1024) bases, runs entirely in your browser.

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

# =============================================================================
#  data-unit-converter.rb — CosmoDev polyglot showcase port of the
#  `data-unit-converter` tool
#  -----------------------------------------------------------------------------
#  Language : Ruby (3.2, standard library only)
#  Source:   ported from src/lib/dataUnits.ts (the canonical, live TypeScript
#             lib); mirrors src/tool-sources/data-unit-converter/{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.
#  -----------------------------------------------------------------------------
#  Pure digital-data unit conversion. Convert between B, KB/KiB, MB/MiB,
#  GB/GiB, TB/TiB: the decimal base 1000 pairs with KB/MB/GB/TB (SI) and the
#  binary base 1024 with KiB/MiB/GiB/TiB (IEC). Deterministic and
#  side-effect free; convert_data returns Float::NAN for non-finite input,
#  unknown unit symbols, or an invalid base — never raises.
# =============================================================================

# Pure digital-data unit conversion for the Data Unit Converter tool on
# CosmoDev (dev.cosmolabs.org).
module DataUnits
  # Units available under each base, ordered smallest (B) to largest. The UI
  # uses this to populate the per-base unit pickers. Frozen — nothing in this
  # module is mutable.
  UNITS = {
    1000 => %w[B KB MB GB TB].freeze,
    1024 => %w[B KiB MiB GiB TiB].freeze,
  }.freeze

  # Power-of-base rank for each unit symbol: "B" is rank 0 and each prefix
  # step adds 1. KB and KiB are both rank 1 — the unit picks the rank, the
  # base decides whether a rank means ×1000 or ×1024.
  EXPONENTS = {
    'B' => 0,
    'KB' => 1, 'MB' => 2, 'GB' => 3, 'TB' => 4,
    'KiB' => 1, 'MiB' => 2, 'GiB' => 3, 'TiB' => 4,
  }.freeze

  module_function

  # Convert a value between two digital-data units under the given base.
  #
  # Strategy: reduce to bytes via value × base**from_exp, then divide by
  # base**to_exp. Returns Float::NAN for non-finite input (NaN or Infinity),
  # unknown unit symbols, or a base other than 1000/1024 — never raises
  # (mirrors the TypeScript contract).
  #
  # value    - the Float magnitude to convert, expressed in from_unit
  # from_unit - source unit symbol, e.g. "MB"
  # to_unit   - target unit symbol, e.g. "GiB"
  # base     - conversion radix: 1000 (decimal/SI) or 1024 (binary/IEC)
  #
  # Returns a Float, or Float::NAN on invalid input.
  def convert_data(value, from_unit, to_unit, base)
    # Reject Inf/NaN early so they don't propagate through the multiplication.
    # Float#finite? is false for both, which keeps the check to one call.
    return Float::NAN unless value.finite?

    # Only the two canonical bases are valid; anything else is a caller bug.
    return Float::NAN unless base == 1000 || base == 1024

    # Unknown unit symbols miss the hash (nil) — treat as invalid.
    from_exp = EXPONENTS[from_unit]
    to_exp = EXPONENTS[to_unit]
    return Float::NAN if from_exp.nil? || to_exp.nil?

    (value * base**from_exp) / base**to_exp
  end
end

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 →