BigInt brings arbitrary-precision integers to Swift without a GMP dependency
Arbitrary-precision arithmetic in pure Swift
At a glance
- What is it?
- attaswift/BigInt is a pure Swift implementation of arbitrary-width integers built on 64-bit digits, with Karatsuba multiplication, Knuth division, modular exponentiation and primality testing. It fits projects that need wide integers without pulling in a C library, at the cost of speed relative to GMP.
- Who is it for?
- BigInt is the right fit for a Swift codebase that needs integers wider than UIntMax and wants to keep its dependency graph free of a C toolchain, particularly for number theory work, key handling and educational use. It is the wrong choice for workloads where GMP level throughput matters, because the author states plainly that the implementation is unlikely to be competitive with GMP.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 65 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Base 2^64 digits stored in Array<UInt64>
The library provides integer types of arbitrary width written entirely in Swift, with no C library underneath. The underlying representation is base 2^64 held in an `Array<UInt64>`, and two types are exposed: `BigUInt` for unsigned values and `BigInt` as the signed variant. Both are Swift structs with copy-on-write value semantics, so they behave like ordinary integers in expressions and pass around without surprise reference sharing.
Internally, `BigUInt` is a collection of its 64-bit digits with the least significant digit at index 0. Its subscript is deliberately forgiving: reading an index at or above `count` returns 0 rather than trapping, and writing to such an index automatically extends the array. The stated purpose is to simplify memory management, which matters when a routine grows a value in place.
`BigInt` is described as a tiny wrapper around a `BigUInt` absolute value plus a sign flag, and both of those are exposed as public read-write properties. That simplicity is a design decision with consequences. Because the magnitude and the sign are separately mutable, code that pokes at them directly can construct an inconsistent state, so treat the public properties as a deliberate escape hatch rather than a convenience to use routinely.
Multiplication flips from schoolbook to Karatsuba past 1024 digits
The arithmetic operators are the ordinary set: `+`, `-`, `*`, `/`, `%` and their in-place forms. Two details make this more than a thin wrapper.
First, addition and subtraction have variants that shift the digits of the second operand on the fly, which is the shape you want for accumulating into sparse or shifted values. Second, unsigned subtraction traps when the result would go negative, and separate variants return an overflow flag instead of trapping, so a caller can handle the underflow deliberately.
Multiplication is where the algorithmic choices show. The implementation uses brute force for numbers up to 1024 digits, then switches to Karatsuba's recursive method. The threshold is not hard coded: it lives in `BigUInt.directMultiplicationLimit`, which is configurable. A fused multiply-add method is also available, along with other special-case variants.
That crossover point is worth thinking about for a different reason than raw speed. A 1024 digit cutoff means most everyday numbers never reach the recursive path, so the asymptotic improvement rarely changes the practical behaviour of typical code. The one case where the limit matters is scientific or cryptographic work on very large values, where raising or lowering the threshold trades memory pressure against speed. The documentation does not give guidance on tuning it, so treat the default as the only tested configuration.
Division follows Knuth Algorithm D and traps on a zero divisor
Division uses Knuth's Algorithm D with its three-halves-digit wide quotient approximation. The documented failure mode is explicit: division traps when the divisor is zero. There is no documented guard or throwing variant in the overview, so a divisor that is computed at runtime and turns out to be zero crashes rather than returning an error. That is a Swift convention rather than a defect, and it is the right default for integer types, but it means the caller is responsible for the check.
There is a separate path worth knowing about: `BigUInt.divide` returns the quotient and the remainder at once, and the documentation states this is faster than computing them separately. For repeated division workloads, such as a base conversion loop, that difference matters because it halves the number of passes over the digit array.
The read-only bitwise properties back this up. `bitWidth` gives the minimum number of bits required to store the integer, `trailingZeroBitCount` the number of trailing zero bits in the binary representation, and `leadingZeroBitCount` the number of leading zero bits when the last digit is not full. Shift operators `>>`, `<<`, `>>=` and `<<=` are present as well, and the full `Comparable` and `Hashable` functionality is implemented, so these types drop into code that already relies on sorting or dictionary keys.
Modular arithmetic covers the usual cryptographic primitives
The library ships the number theory functions that make it usable beyond plain accounting of large magnitudes. `base.power(exponent, modulus)` performs modular exponentiation using the right-to-left binary method, and a vanilla exponentiation is also available for cases where no modulus applies. `n.inverse(modulus)` computes the multiplicative inverse in modular arithmetic with the extended Euclidean algorithm, and `BigUInt.gcd(n, m)` returns the greatest common divisor using Stein's algorithm.
Primality testing is handled by `n.isPrime()`, which implements the Miller-Rabin test. The samples referenced in the write up include an RSA cryptography example and a calculation of the digits of pi, which together indicate the intended range: this is aimed at people who need the primitives themselves rather than at people who only need a large number type.
`n.inverse(modulus)` deserves one caution. The extended Euclidean algorithm produces an inverse only when the two values are coprime, and the overview does not describe what happens otherwise. Do not assume the method validates its precondition for you; if you cannot guarantee coprimality, check it before calling.
Beyond number theory, there are conversions between `NSData` and big integers in both directions, random integer generation at a specified maximum width or magnitude, and radix conversion to and from `String` and big integers up to base 36 using repeated divisions. `StringLiteralConvertible` is implemented in base 10.
No generic BigInt<Digit> because Swift generics cost runtime
One section of the write up is devoted to a single absence: there is no generic `BigInt<Digit>` type. The types are not parametric, and the reason given is performance rather than taste. Swift generics cost runtime in this use case, and the author states that every approach tried to make arbitrary-precision arithmetic generic was slower.
That is a defensible trade for a library whose entire value proposition is speed relative to a C library, but it removes an option some developers expect. You cannot parameterise the digit width, so there is no way to ask for 32-bit limbs or to share code between the unsigned and signed type at the type level. The signed type is in fact not a subclass of the unsigned one; it is a separate struct that wraps it, and the relationship between them is expressed by composition rather than inheritance.
The second half of that choice shows up in how the two types are used together. Code that frequently mixes signed and unsigned big values has to convert explicitly, and any future digit type would have introduced a third conversion path. The trade favours the common case, where a project uses one or the other, over the mixed case.
A related note: the module uses very few platform-provided APIs, which the author gives as the reason it should be portable to other operating systems. That claim is untested in the documentation, so treat it as a plausible path rather than a supported one.
Swift Package Manager integration is described as experimental
Setup is a single dependency entry. The documented instructions describe Swift Package Manager support as experimental, and the manifest snippet to add is:
.package(url: "https://github.com/attaswift/BigInt.git", from: "5.4.0")That line is placed in the dependency section of your `Package.swift` manifest. The pinned lower bound in the snippet is 5.4.0, which is also the last release listed as supporting the 5.x line of Swift in the compatibility table.
That table is the more important part of the setup story, because it maps Swift toolchain versions to the last compatible BigInt release: 3.x pairs with 2.1.0, 4.0 with 3.1.0, 4.2 with 4.0.0, and 5.x with 5.4.0. BigInt 4.0.0 requires Swift 4.2, and the write up notes that BigInt 2.1.0 was the last version supporting Swift 3.x while 1.3.0 was the last for Swift 2. If your project is pinned to an older toolchain, the release you need is not the release you get by default.
Deployment targets are macOS 10.10, iOS 9, watchOS 2 and tvOS 9. The author notes that testing covered only the latest OS releases, while arguing that the module uses very few platform APIs so earlier versions should present little difficulty. A first real use is small: import the module, construct a `BigUInt` or `BigInt`, and call `isPrime()` or `base.power(exponent, modulus)` to see the number theory paths work.
MIT licence, 2026 releases, and GMP as the honest yardstick
BigInt can be used, distributed and modified under the MIT licence, which is permissive and short enough to read in full before shipping it in a closed source product. That removes the licence question that hangs over libraries relicensed under copyleft or bespoke terms.
On maintenance, the version numbers tell a clear story. The releases include v6.0.0 on 2026-06-27 and v6.0.1 on 2026-08-19, with v5.7.0 before that on 2025-07-26. The last push to the repository was on 2026-08-19, so the code has been receiving commits within the last few months and the repository is not archived. The spacing between 5.7.0 and 6.0.0 suggests the major version boundary was used for something, likely the removal of older Swift support given the compatibility table, but the write up does not include a changelog narrative.
On performance the documentation is refreshingly blunt. The implementations are intended to be reasonably efficient, but they are unlikely to be competitive with GMP at all, even where the same asymptotic behaviour is implemented. The author adds that no comparison benchmark has been performed. The intended niche is stated directly in the overview: handy when you need an integer wider than `UIntMax` but do not want GMP as a dependency. On throughput critical work, GMP remains the reference point.
The test suite is described as having 100% unit test coverage, and the write up immediately qualifies that with the observation that this does not imply there are no bugs. Read that as a statement of confidence in coverage rather than in correctness.
Editorial conclusion
BigInt is the right fit for a Swift codebase that needs integers wider than UIntMax and wants to keep its dependency graph free of a C toolchain, particularly for number theory work, key handling and educational use. It is the wrong choice for workloads where GMP level throughput matters, because the author states plainly that the implementation is unlikely to be competitive with GMP. Before adopting, check the version table for your Swift toolchain, since BigInt 4.0.0 and later require Swift 4.2 and the Package Manager integration is described as experimental, and confirm that your platform falls inside the stated deployment targets of macOS 10.10, iOS 9, watchOS 2 and tvOS 9.
Frequently asked questions
How do I add BigInt to a Swift project?
Add the package to the dependency section of your Package.swift manifest as documented in the setup instructions, with the .package(url:from:) entry pointing at the GitHub repository. The write up describes Swift Package Manager support as experimental.
Which Swift version does the current BigInt release require?
BigInt 4.0.0 and later require Swift 4.2, and the compatibility table lists 5.4.0 as the last release for the 5.x line. Older toolchains need older releases, with 2.1.0 for Swift 3.x and 1.3.0 for Swift 2.
Is BigInt faster than GMP?
The documentation states that the implementations are unlikely to be competitive with GMP at all, even where the same asymptotic behaviour is used. The author notes that no comparison benchmark has been performed.
How do I compute a modular inverse with BigInt?
The n.inverse(modulus) method computes a multiplicative inverse in modular arithmetic using the extended Euclidean algorithm. Modular exponentiation is available through base.power(exponent, modulus), which uses the right-to-left binary method.
Why does BigInt not offer a generic BigInt<Digit> type?
The omission is intentional. The write up says Swift generics cost runtime in this use case, and that every approach tried to make arbitrary-precision arithmetic generic was slower in practice.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/attaswift-bigint)Was this useful?