Dart BigInt
last modified May 30, 2026
In Dart, BigInt is a data type that represents arbitrary-precision
integers, allowing developers to work with numbers larger than 64 bits. Unlike
standard int, which has a fixed size limit based on the system
architecture, BigInt can store extremely large values without
overflow, making it essential for applications requiring high-precision
calculations.
BigInt is particularly useful in domains where large numerical
values are common, such as:
- Cryptography - Secure encryption algorithms often require large prime numbers.
- Scientific computing - Simulating astronomical distances or molecular structures.
- Blockchain and hashing - Managing large cryptographic hashes and blockchain computations.
BigInt is part of Dart's core library, so it requires no
additional dependencies. The class provides a rich set of operations including:
- Basic arithmetic: addition (+), subtraction (-), multiplication (*), integer division (~/)
- Bitwise operations: AND (&), OR (|), XOR (^), shifts (<<, >>)
- Modular arithmetic:
modPowfor fast exponentiation,modInversefor modular inversion - Number theory helpers:
gcd,pow,abs - Comparison and properties:
compareTo,isEven,isOdd,isNegative,sign,bitLength - Conversions:
toInt,toDouble,toStringwith optional radix
By leveraging BigInt, Dart developers can perform precise
calculations without integer overflow, making it a crucial tool for
cryptography, scientific computing, and blockchain applications.
Creating BigInt Values
BigInt values can be created in several ways: from an integer
literal with BigInt.from, by parsing a decimal string with
BigInt.parse, or by using the predefined constants
BigInt.zero, BigInt.one, and BigInt.two.
void main() {
var big1 = BigInt.from(123456789);
var big2 = BigInt.parse('98765432109876543210');
var big3 = BigInt.zero;
var big4 = BigInt.one;
var big5 = BigInt.two;
print(big1);
print(big2);
print(big3);
print(big4);
print(big5);
}
BigInt.from converts a Dart int to BigInt,
while BigInt.parse accepts any decimal string regardless of size.
The constants BigInt.zero, BigInt.one, and
BigInt.two are convenient pre-built values for common initial
states such as accumulators or counters.
$ dart main.dart 123456789 98765432109876543210 0 1 2
Parsing with Radix
BigInt.parse accepts an optional radix parameter,
allowing values to be parsed from hexadecimal, binary, octal, or any other
base between 2 and 36. The toString method similarly accepts a
radix for formatting output.
void main() {
var fromHex = BigInt.parse('DEADBEEFCAFE', radix: 16);
var fromBin = BigInt.parse('11001010111111101101010', radix: 2);
var fromOct = BigInt.parse('755', radix: 8);
print('Hex -> decimal: $fromHex');
print('Binary -> decimal: $fromBin');
print('Octal -> decimal: $fromOct');
var n = BigInt.parse('123456789012345678901234567890');
print('Decimal: $n');
print('Hex: ${n.toRadixString(16)}');
print('Binary: ${n.toRadixString(2)}');
print('Base 36: ${n.toRadixString(36)}');
}
When radix is omitted, the default is 10 (decimal). Hex input
must not include the 0x prefix; strip it first if necessary.
toRadixString is a convenient shorthand for
toString(radix: …).
$ dart main.dart Hex -> decimal: 244838016843006 Binary -> decimal: 6647146 Octal -> decimal: 493 Decimal: 123456789012345678901234567890 Hex: 5ce0d6380b2a6ea3cb43c303d2 Binary: 101110011100000110101100011100000001011001010100110111010100011110010110100001111000011000000111101 0010 Base 36: 9ugzjoynhxzflyxynqmei
Converting BigInt
BigInt can be converted to int, double,
or String. When converting to int, values that
exceed the platform's integer range are truncated, so always check
isValidInt first.
void main() {
var small = BigInt.from(42);
var huge = BigInt.parse('99999999999999999999999999999999');
// BigInt -> int
print('small.isValidInt: ${small.isValidInt}');
print('small.toInt(): ${small.toInt()}');
print('huge.isValidInt: ${huge.isValidInt}');
// BigInt -> double (loses precision for very large values)
print('huge.toDouble(): ${huge.toDouble()}');
// BigInt -> String
print('small.toString(): ${small.toString()}');
print('small.toRadixString(2): ${small.toRadixString(2)}');
print('small.toRadixString(16): ${small.toRadixString(16)}');
// int -> BigInt
int n = 999;
BigInt b = BigInt.from(n);
print('int to BigInt: $b');
}
isValidInt returns true when the value fits into a
64-bit signed integer (the native Dart VM integer type). For very large
BigInt values, toDouble may lose precision because
double is a 64-bit floating-point type with only 53 mantissa bits.
$ dart main.dart small.isValidInt: true small.toInt(): 42 huge.isValidInt: false huge.toDouble(): 1e+32 small.toString(): 42 small.toRadixString(2): 101010 small.toRadixString(16): 2a int to BigInt: 999
Basic Arithmetic Operations
BigInt supports all standard arithmetic operations: addition, subtraction,
multiplication, integer division, and remainder. The integer division operator
~/ truncates the result; regular / is not available
because it would require a double intermediate which cannot
represent all BigInt values exactly.
void main() {
var a = BigInt.parse('12345678901234567890');
var b = BigInt.parse("987654321");
print('Addition: ${a + b}');
print('Subtraction: ${a - b}');
print('Multiplication: ${a * b}');
print('Division: ${a ~/ b}');
print('Remainder: ${a % b}');
}
We perform basic arithmetic on two large BigInt values. The ~/
operator performs truncating integer division; the result drops any fractional
part. The % operator returns the non-negative remainder.
$ dart main.dart Addition: 12345678902222222211 Subtraction: 12345678900246913569 Multiplication: 12193263112482853211126352690 Division: 12499999887 Remainder: 339506163
Absolute Value and Sign
BigInt fully supports negative values. The abs method
returns the absolute value, sign returns -1,
0, or 1, and the unary minus operator negates a value.
void main() {
var pos = BigInt.parse('123456789012345678901234567890');
var neg = -pos;
print('pos: $pos');
print('neg: $neg');
print('neg.abs(): ${neg.abs()}');
print('pos.sign: ${pos.sign}');
print('neg.sign: ${neg.sign}');
print('BigInt.zero.sign: ${BigInt.zero.sign}');
print('neg.isNegative: ${neg.isNegative}');
}
The unary minus operator creates the negative counterpart. abs
always returns a non-negative value, while sign compactly
encodes whether the number is negative, zero, or positive.
$ dart main.dart pos: 123456789012345678901234567890 neg: -123456789012345678901234567890 neg.abs(): 123456789012345678901234567890 pos.sign: 1 neg.sign: -1 BigInt.zero.sign: 0 neg.isNegative: true
Comparison and Properties
BigInt values can be compared using standard operators. They also provide properties like isEven, isOdd, and sign.
void main() {
var x = BigInt.parse('12345678901234567890');
var y = BigInt.parse('98765432109876543210');
print('x < y: ${x < y}');
print('x == y: ${x == y}');
print('x.isEven: ${x.isEven}');
print('y.isOdd: ${y.isOdd}');
print('x.sign: ${x.sign}');
print('y.bitLength: ${y.bitLength}');
}
We compare two BigInt values and inspect their properties.
bitLength returns the minimum number of bits needed to represent
the absolute value of the number (excluding the sign bit). For example, 8
requires 4 bits (1000₂).
$ dart main.dart x < y: true x == y: false x.isEven: true y.isOdd: false x.sign: 1 y.bitLength: 67
Bitwise Operations
BigInt supports bitwise operations like AND, OR, XOR, and shifts. These are useful for low-level programming and cryptography.
void main() {
var a = BigInt.parse('0xFF00FF00FF00FF00FF');
var b = BigInt.parse('0x00FF00FF00FF00FF00');
print('AND: ${a & b}');
print('OR: ${a | b}');
print('XOR: ${a ^ b}');
print('NOT a: ${~a}');
print('Shift left: ${a << 4}');
print('Shift right: ${a >> 8}');
}
We perform various bitwise operations on hexadecimal BigInt values.
Note that ~a (bitwise NOT) on a non-negative BigInt
produces a negative result, consistent with two's complement semantics.
$ dart main.dart AND: 0 OR: 4722366482869645213695 XOR: 4722366482869645213695 NOT a: -4703991516010230251776 Shift left: 75263864256163684028400 Shift right: 18374966859414961920
GCD and Number Theory
The gcd method computes the greatest common divisor of two
BigInt values — an essential building block in many number-theory
algorithms. It is used, for example, to verify that two numbers are coprime
before computing a modular inverse.
void main() {
var a = BigInt.parse('123456789012345678901234567890');
var b = BigInt.parse('987654321098765432109876543210');
print('gcd(a, b): ${a.gcd(b)}');
// Check coprimality
var p = BigInt.from(17);
var q = BigInt.from(35);
print('gcd(17, 35): ${p.gcd(q)}');
print('17 and 35 are coprime: ${p.gcd(q) == BigInt.one}');
// Reduce a fraction
var num = BigInt.from(360);
var den = BigInt.from(840);
var g = num.gcd(den);
print('360/840 reduced: ${num ~/ g}/${den ~/ g}');
}
gcd(a, b) always returns a non-negative value. Two integers are
coprime when their GCD equals 1 — a requirement for modInverse to
exist. The fraction-reduction example shows a practical real-world use.
$ dart main.dart gcd(a, b): 90000000000000000000 gcd(17, 35): 1 17 and 35 are coprime: true 360/840 reduced: 3/7
Modular Arithmetic
BigInt provides methods for modular arithmetic, including pow and
modPow which are essential for cryptographic algorithms.
void main() {
var base = BigInt.from(5);
var exponent = BigInt.from(100);
var modulus = BigInt.from(101);
print('5^100: ${base.pow(100)}');
print('5^100 mod 101: ${base.modPow(exponent, modulus)}');
print('Modular inverse of 5 mod 101: ${base.modInverse(modulus)}');
}
We demonstrate exponentiation and modular operations. modPow
uses fast modular exponentiation (square-and-multiply) to handle enormous
exponents efficiently. modInverse finds the modular multiplicative
inverse — i.e., the value x such that (base * x) % modulus == 1.
A modular inverse only exists when the base and modulus are coprime.
$ dart main.dart 5^100: 7888609052210118054117285652827862296732064351090230047702789306640625 5^100 mod 101: 1 Modular inverse of 5 mod 101: 81
Factorial
Computing factorials of large numbers is a classic use case for
BigInt. Even modest inputs like 100! produce values far beyond the
range of a 64-bit integer.
BigInt factorial(int n) {
if (n <= 1) return BigInt.one;
return BigInt.from(n) * factorial(n - 1);
}
void main() {
for (var n in [10, 20, 50, 100]) {
print('$n! = ${factorial(n)}');
}
}
The function uses BigInt.one as the base case and multiplies
BigInt.from(n) at each step. Standard Dart int would
overflow around 20!, but BigInt handles any size effortlessly.
$ dart main.dart 10! = 3628800 20! = 2432902008176640000 50! = 30414093201713378043612608166979581188299763898377856340086048154919691977786015699310976576 100! = 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
Large Fibonacci Numbers
The Fibonacci sequence grows exponentially. Computing high-indexed Fibonacci
numbers quickly exceeds 64-bit integer limits, making BigInt the
right tool.
BigInt fibonacci(int n) {
var a = BigInt.zero;
var b = BigInt.one;
for (var i = 0; i < n; i++) {
var temp = a + b;
a = b;
b = temp;
}
return a;
}
void main() {
for (var n in [10, 50, 100, 500]) {
var f = fibonacci(n);
print('fib($n) = $f');
print(' digits: ${f.toString().length}');
}
}
The iterative approach avoids recursion overhead and stack limits. At
n = 500 the result has over 100 decimal digits — impossible with
native integers.
$ dart main.dart fib(10) = 55 digits: 2 fib(50) = 12586269025 digits: 11 fib(100) = 354224848179261915075 digits: 21 fib(500) = 139423224561697880139724382870407283950070256587697307264108962948325571622863290691557658876222521294125 digits: 105
Best Practices
- Prefer
intwhen possible:BigIntarithmetic is significantly slower than native integer arithmetic; only use it when values may exceed 2⁶³ − 1. - Memory overhead: Each
BigIntinstance allocates a variable-length byte array; avoid creating many short-livedBigIntobjects in tight loops. - Guard conversions: Always call
isValidIntbeforetoInt()to avoid silent truncation of large values. - Handle parse errors:
BigInt.parsethrows aFormatExceptionon invalid input; useBigInt.tryParsewhen the input is untrusted and handle the possiblenullreturn. - Coprimality before
modInverse: CallingmodInverseon values that share a common factor throws anException; verify withgcd == BigInt.onefirst. - Prefer
modPowover manual exponentiation:base.modPow(exp, mod)is both faster and memory-efficient compared to computingbase.pow(exp) % modfor large exponents.
Source
This tutorial covered Dart's BigInt with practical examples
demonstrating creation, radix parsing, type conversions, arithmetic,
properties, bitwise operations, GCD, modular arithmetic, and real-world use
cases such as factorial and large Fibonacci numbers.
Author
List all Dart tutorials.