Contents

ruinelson/swiftengineeringnumberformatter

A powerful and flexible Swift library for formatting numbers using engineering notation with [SI metric prefixes](https://en.wikipedia.org/wiki/Metric_prefix). Supports all multiples of 3 SI metric prefixes from quecto (10⁻³⁰) to quetta (10³⁰).

Features

  • Complete SI Prefix Support: All 20 standard metric prefixes
  • High Precision: Full Decimal type support for exact arithmetic
  • Thread Safe: Sendable conformance for concurrent environments
  • Locale Aware: Respects decimal separators and number formatting rules
  • Highly Configurable: Fraction digits, spacing, micro symbol options
  • Swift 6 Ready: Uses modern Swift Testing framework
  • Zero Dependencies: Lightweight with no external dependencies

Supported Prefixes

| Prefix | Symbol | Factor | Example | | ------ | ------ | ------ | ------------------------------------------------ | | quetta | Q | 10³⁰ | 1Q = 1,000,000,000,000,000,000,000,000,000,000 | | ronna | R | 10²⁷ | 1R = 1,000,000,000,000,000,000,000,000,000 | | yotta | Y | 10²⁴ | 1Y = 1,000,000,000,000,000,000,000,000 | | zetta | Z | 10²¹ | 1Z = 1,000,000,000,000,000,000,000 | | exa | E | 10¹⁸ | 1E = 1,000,000,000,000,000,000 | | peta | P | 10¹⁵ | 1P = 1,000,000,000,000,000 | | tera | T | 10¹² | 1T = 1,000,000,000,000 | | giga | G | 10⁹ | 1G = 1,000,000,000 | | mega | M | 10⁶ | 1M = 1,000,000 | | kilo | k | 10³ | 1k = 1,000 | | — | — | 10⁰ | 1 = 1 | | milli | m | 10⁻³ | 1m = 0.001 | | micro | µ/u | 10⁻⁶ | = 0.000001 | | nano | n | 10⁻⁹ | 1n = 0.000000001 | | pico | p | 10⁻¹² | 1p = 0.000000000001 | | femto | f | 10⁻¹⁵ | 1f = 0.000000000000001 | | atto | a | 10⁻¹⁸ | 1a = 0.000000000000000001 | | zepto | z | 10⁻²¹ | 1z = 0.000000000000000000001 | | yocto | y | 10⁻²⁴ | 1y = 0.000000000000000000000001 | | ronto | r | 10⁻²⁷ | 1r = 0.000000000000000000000000001 | | quecto | q | 10⁻³⁰ | 1q = 0.000000000000000000000000000001 |

Quick Start

Basic Usage with Extensions

The simplest way to get started is using the convenience extensions:

import SwiftEngineeringNumberFormatter

// Double conversions
let value = Double(engineeringNotation: "2.2k")  // Optional(2200.0)
let formatted = (1500.0).engineering              // "1.5k"

// Decimal conversions (high precision)
let decimal = Decimal(engineeringNotation: "333n") // Optional(0.000000333)
let precision = decimal!.engineering                // "333n"

Formatter Instance

For more control, create a formatter instance:

let formatter = EngineeringNumberFormatter()

// Double formatting
let result1 = formatter.string(1_000_000.0)        // "1M"
let parsed1 = formatter.double("2.5G")             // Optional(2500000000.0)

// Decimal formatting (recommended for precision)
let result2 = formatter.string(decimal: Decimal(-1_000_000))  // "-1M"
let parsed2 = formatter.decimal("10p")             // Optional(0.00000000001)

Advanced Configuration

Customize the formatter to match your requirements:

let formatter = EngineeringNumberFormatter(
    minimumFractionDigits: 1,     // Always show at least 1 decimal place
    maximumFractionDigits: 3,     // Limit to 3 decimal places
    locale: Locale(identifier: "de_DE"),  // German locale (comma separator)
    useGreekMu: false,            // Use 'u' instead of 'µ' for micro
    addSpace: true               // Add space between number and prefix
)

let result = formatter.string(2.222222e-6)  // "2,222 u" (German formatting)

Precision Matters

When working with high-precision calculations, use Decimal to avoid floating-point arithmetic issues:

let formatter = EngineeringNumberFormatter()

// Double precision limitations
let doubleSum = formatter.double("0.2")! + formatter.double("100m")!
// doubleSum != 0.3 (floating point precision issues)

// Decimal precision advantage
let decimalSum = formatter.decimal("0.2")! + formatter.decimal("100m")!
// decimalSum == 0.3 (exact decimal arithmetic)

// High precision example
let highPrecision = formatter.decimal("1M")! + formatter.decimal("1f")!
// Exactly: 1000000.000000000000001

Error Handling

The library gracefully handles invalid inputs:

let formatter = EngineeringNumberFormatter()

// These return nil for invalid inputs
formatter.double("")           // nil
formatter.double("invalid")    // nil  
formatter.double("1.2.3k")     // nil
formatter.decimal("µ")         // nil

Installation

Swift Package Manager

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/RuiCarneiro/SwiftEngineeringNumberFormatter", from: "3.0.0")
]

Add to your target dependencies:

.target(
    name: "YourTarget",
    dependencies: ["SwiftEngineeringNumberFormatter"]
)

Xcode

  1. File → Add Package Dependencies
  2. Enter: https://github.com/RuiCarneiro/SwiftEngineeringNumberFormatter
  3. Select version and add to your target

Requirements

  • Swift 6.0+
  • iOS 13.0+ / macOS 10.15+ / watchOS 6.0+ / tvOS 13.0+
  • Linux (Swift Package Manager)

Thread Safety

EngineeringNumberFormatter is marked as Sendable and is safe to use across multiple threads concurrently.

Performance

The library is optimized for performance with:

  • Efficient string parsing algorithms
  • Minimal memory allocations
  • Built-in caching for number formatters
  • Comprehensive test suite including performance benchmarks

Testing

The project uses Swift Testing framework (Swift 6.0+):

swift test                    # Run all tests
swift build                   # Build the package

Documentation

Generate documentation using Swift-DocC:

swift package generate-documentation

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Licensed under the Apache License, Version 2.0. See LICENSE for details.

Package Metadata

Repository: ruinelson/swiftengineeringnumberformatter

Default branch: main

README: README.md