Swift Numerics Package Overview
Provides building blocks for generic numerical computing in Swift.
High-performance
Float16type.The package is open-source at github.com/apple/swift-numerics
Example: The Logit function
This example uses the logit function from statistics.
Essentially, given a probability
pin the range 0…1, returns the log of the odds,log(p / (1 - p)).
You would implement the function like this:
import Darwin
func logit(_ p: Double) -> Double {
log(p) - log1p(-p)
}There’s a small problem: this only supports Doubles. What about Floats or any other floating-point type? Our function should try to be generic.
A reasonable first attempt might look like:
import Darwin
func logit<T>(_ p: T) -> T {
log(p) - log1p(-p)
}But this won’t compile because the log and log1p functions only make sense for a handful of floating-point types.
We need a constraint for our generic type. The Numerics module provides a Real protocol, which allows our code to work for any standard floating-point type, current or future. This code calls generic versions of the log and log1p functions.
import Numerics
func logit<NumberType: Real>(_ p: NumberType) -> NumberType {
log(p) - log1p(-p)
}The Real protocol
Part of Swift Numerics package
Provides generic access to all standard floating-point capabilities.
The definition is:
public protocol Real: FloatingPoint, AlgebraicField, RealFunctions { ... }Note that AlgebraicField and RealFunctions are also new protocols defined in the Numerics package. However, Real is the one you should usually use.
Numeric Protocols in the Swift standard library
These are the protocols already defined in the Swift standard library:

This topic focuses on AdditiveArithmetic, SignedNumeric, and FloatingPoint
AdditiveArithmeticapplies to types that support addition and subtraction.SignedNumericintroduces multiplication.FloatingPointadds many other operations for floating-point computation. This includes comparison functions, a way to decompose numbers into an exponent and significand, and useful constants like infinity or pi.
Protocols in the Numerics package
The Numerics package builds on protocols already in the Swift standard library.
AlgebraicFieldaugmentsSignedNumericby introducing division and reciprocation.ElementaryFunctionsaugmentsAdditiveArithmeticby adding a large collection of common floating-point functions, such as logarithms and trigonometric functions.RealFunctionsextendsElementaryFunctionseven further with some lesser-used functions, such as gamma and error functions.
The Real protocol combines all of these protocols into a single, unified concept:

Implications of the Real protocol
Generics constrained to
Realsupport all standard floating-point types.Reduces code duplication.
Simplifies maintenance.
Makes defining new numerical types easier.
The Complex type
Part of the Swift Numerics package
Generic over any
Realtype, so it works for any floating-point type.
import Numerics
let z = Complex(1.0, 2.0) // z = 1 + 2iComplex defaults to Double, so the full type-annotated version of the code is:
import Numerics
let z: Complex<Double> = Complex(1.0, 2.0) // z = 1 + 2iGeneric Numerical Programming
While Complex is a type by itself, it also enables generic numerical programming.
public struct Complex<NumberType> where NumberType: Real {
/// The real component
public var real: NumberType
/// The imaginary component
public var imaginary: NumberType
/// Construct a complex number with specified real and imaginary parts
public init (_ real: NumberType, imaginary: NumberType) {
self.real = real
self.imaginary = imaginary
}
}
To make complex numbers fully functional, we need to extend them with the SignedNumeric protocol:
extension Complex: SignedNumeric {
/// The sum of 'z' and 'w'
public static func +(z: Complex, w: Complex) -> Complex {
Complex(z.real + w.real, z.imaginary + w.imaginary)
}
/// The difference of 'z' and ' w'
public static func -(z: Complex, w: Complex) -> Complex {
Complex (z.real - w.real, z.imaginary - w.imaginary)
}
/// The product of 'z' and
public static func *(z: Complex, w: Complex) -> Complex {
Complex(
z.real * w.real - z.imaginary * w.imaginary,
z.real * w.imaginary + z.imaginary * w.real
)
}
}
Complex numbers are often expressed in polar coordinates (length + phase angle):
extension Complex {
/// The Euclidean norm (a.k.a. 2-norm) of the number.
public var length: NumberType {
.hypot(real, imaginary)
}
/// The phase (angle, or "argument").
///
/// Returns the angle (measured above the real axis) in radians.
public var phase: NumberType {
.atan2(y: imaginary, x: real)
}
/// A complex value with specified polar coordinates.
public init(length: Number Type, phase: Number Type) {
self = Complex (.cos(phase), .sin(phase)).multiplied(by: length)
}
}
Compatibility with C and C++
Complex is a plain struct with 2 floating-point values, so its memory layout precisely matches the complex number types of C and C++. The following are all the same in memory:
Complex<Double>(Swift)_Complex double(C)std::complex<double>(C++)
As a result, Swift code can exchange buffers of complex numbers with C/C++ APIs. You can just pass a pointer to a Swift Complex struct to a C/C++ library that also expects a complex type.
Example: Using Accelerate’s BLAS functions
BLAS = Basic Linear Algebra Subroutines. Apple’s implementation is written in C. Notice how the function accepts a pointer to the array of Complex<Double> numbers using the ampersand (&) operator.
import Numerics
import Accelerate
// Array of 100 random `Complex<Double>` numbers.
let z = (0 ..< 100).map {
Complex(length: 1.0, phase: Double.random(in: -.pi ... .pi))
}
// Compute the Euclidean norm of `z`.
let norm = cblas_dznrm2(z.count, &z, 1)One Caveat
The Numerics package treats complex infinity and NaN differently than in C or C++. This can affect code ported from C or C++.
However, Swift’s treatment of these values is simpler and significantly more performant for multiplication and division. Below is a benchmark comparing Swift to C:

Numerics is a work in progress
Recent additions:
Specialized handling for integer powers.
Approximate equality.
Numerics is developed as a community on GitHub. Some of the projects being discussed include:
Arbitrary-precision integers.
Shaped arrays.
Decimal floating point.
Float16
Is a new floating-point type in the Swift standard library.
IEEE 754 standard format.
Already available on iOS, iPadOS, tvOS, watchOS (ARM-based platforms).
Calling convention for x86 is not yet finalized (working with Intel to fix this).
Is a normal floating-point type.
Conforms to
BinaryFloatingPointandSIMDScalar.Conforms to
Realfrom Swift Numerics.Supports all of the standard floating-point functions.
If you implement the Real protocol in your code, you will automatically get support for Float16.
Trade-offs
Pros:
Significant performance gains because you can fit more of them into a SIMD register or a page of memory.
Interoperates with C/Objective-C
__fp16type.
Cons:
Low precision, small range.
Size and Constraints
Be mindful of these constraints when porting code that was originally written with Float or Double in mind:

Hardware Support
Supported (and preferred) by Apple’s GPUs.
Supported by Apple’s CPUs starting with A11 Bionic.
Scalar performance identical to
FloatorDouble.SIMD performance is 2x that of
Float.
Float16is simulated usingFloatoperations on older hardware. The results are exactly the same, but without the speedup gains.
Here’s a benchmark of Float16 against Float:

Getting involved with Swift Numerics
Visit the GitHub page at github.com/apple/swift-numerics
Participate in Swift forums under the category “Related Projects”
