Every Built-in Method: int, double, num, bool, BigInt and dart:math
After this lesson you will see how a number is stored inside the machine (64 bits for an int, sign + exponent + mantissa for a double, a list of 32-bit digits for a BigInt), why 0.1 + 0.2 is not 0.3, why int arithmetic silently wraps around, and you will have met every public member of num, int, double, bool, BigInt, Comparable, Random and the dart:math functions and constants, each with its cost, its exceptions and a runnable example whose exact output was checked against the real Dart SDK.
Related lessons: D03 numbers and bits (the basics this lesson goes deeper than), D22 mutable vs immutable, D26 List, and C31 number-theoretic algorithms (gcd, modPow and modInverse in depth). Source of truth: the Dart SDK 3.11 sources (core/num.dart, int.dart, double.dart, bool.dart, bigint.dart, comparable.dart, math/math.dart, math/random.dart and the VM implementations). Everything below describes the native VM / AOT unless it says web; section 7 lists the differences, measured by compiling the same code with dart compile js. Every printed result on this page was produced by running the code on Dart 3.11 and is asserted in verify/d23.dart.
1. What a number really is (memory picture)
Think of a number as a row of light switches. A bit is one switch: off (0) or on (1). An int in Dart is exactly 64 switches in a row. A double also uses 64 switches, but split into three groups with different jobs (a sign switch, 11 switches for "how big", 52 switches for "which digits"), like scientific notation -6.25 × 100 written in binary. A BigInt is a stack of 32-switch boards: when the number needs more room, you simply add another board. The price: a fixed row is one CPU instruction to use; a stack of boards needs a loop.
Words, defined once. A bit is a 0 or a 1. A byte is 8 bits. Two's complement is the way computers write negative whole numbers in bits (section 2). A mantissa is the "digits" part of a floating-point number. An exponent says how far to slide the point. Overflow means a result is too big for the bits available. Immutable means the value can never change after it is made: every number operation makes a new number.
| Type | Stored as | Range / precision | Cost of + |
|---|---|---|---|
int | 64-bit two's complement (native); a JavaScript number (web) | -263 .. 263-1 exactly; wraps silently | O(1), one instruction |
double | IEEE-754 binary64: 1 sign + 11 exponent + 52 mantissa bits | about ±1.8×10308, 15-17 significant decimal digits, rounded | O(1), one instruction |
BigInt | sign flag + list of 32-bit digits (native), 16-bit digits (web) | unlimited; exact | O(n) in the number of digits |
bool | one of two shared objects true and false | 2 values; not a number, not Comparable | O(1) |
num | nothing: it is the sealed parent type of int and double | whatever its child holds | - |
The next animation builds a BigInt in memory: the variable on the stack (a small scratch area for local variables) holds only an arrow to an object on the heap (the big shared storage room for objects). The object holds the sign, the number of digits in use and an arrow to the list of 32-bit digits, least significant digit first. Type your own number to see how many digits it needs and whether isValidInt / toInt work. Input size → feasible: up to a few thousand digits everything is instant; a 105-digit BigInt makes toString and parse (O(n2)) take noticeable time.
Input: a whole number of at most 60 digits, optionally starting with -.
int or double variable holds its value directly (or an arrow to a tiny boxed number, an implementation detail of the VM); a BigInt is a real object with a list inside. Numbers are values: a = b copies the value, and there is no way to change a number in place.2. Integers: 64 bits, two's complement, shifts and overflow
A car odometer with 5 wheels counts 00000 ... 99999 and then rolls over to 00000, with no warning. A native Dart int is an odometer with 64 binary wheels: after the biggest number comes the smallest negative one. Two's complement is how the same wheels also show negative numbers: the leftmost wheel (bit 63) counts as -263 instead of +263, so a 1 there means "negative". To negate a number, flip all bits and add 1.
The bit lab below runs real Dart statements on x, a 64-bit int, and shows all 64 bits, bit 63 on the left (the top row of 32 boxes holds bits 63..32, the bottom row bits 31..0). A green box is a 1. Outlined boxes are the bits the step changed or filled. Watch for four things: negation by flip-and-add-1, the three shifts (<<, >> copies the sign bit, >>> fills with zeros), the odometer roll-over, and toSigned / toUnsigned cutting a number down to a narrower width.
Your own bit lab. Format: a start value, then up to 10 operations separated by ;. Values: whole numbers (-9223372036854775808 .. 9223372036854775807) or hex like 0xFF. Operations: << n, >> n, >>> n (n from 0 to 70), & v, | v, ^ v, + v, - v, * v, ~, neg, toSigned w, toUnsigned w (w from 1 to 64), set v.
Where the odometer really bites, with the exact edge values (each line was run on the native VM; no exception is thrown by any of them):
factorial(21), pow(10, 19), a * b for two big numbers, a hash computed with *, an average computed as (lo + hi) / 2 in binary search (use lo + (hi - lo) ~/ 2) can all wrap around and give a plausible but wrong answer. When a result can exceed 9.2×1018 use BigInt, check with a bound first, or do the arithmetic modulo a prime with modPow. Input size → feasible: products of two numbers up to 3×109 are safe in an int; up to 1018 you can add a few; beyond that use BigInt.int is a JavaScript number: exact only up to 253, and the bit operators work on 32 bits (1 << 32 is 0 there). Integer literals above 253 are a compile error. Section 7 shows measured differences.3. Doubles: sign, exponent, mantissa (and 0.1 + 0.2)
Scientific notation writes 6250 as 6.25 × 103: some digits and a power of ten. A double does the same in binary: a sign, a power of two (the exponent) and 53 binary digits (the mantissa; the first digit is always 1, so only 52 are stored). Like a ruler whose marks get farther apart as the numbers grow, doubles are dense near 0 and sparse far away; most decimals such as 0.1 fall between two marks, and the computer keeps the nearest mark.
The first animation takes any number apart into its three fields (type any decimal, or nan, inf, -0). The exact decimal value stored is shown at the end: it is rarely the text you typed.
Input: a decimal number such as 0.1, -6.25, 1e-7, or nan, inf, -inf, -0.
The five special doubles of double.nan, infinity, negativeInfinity, minPositive and maxFinite, and negative zero, each decoded (use the arrows):
Now the famous sum. The CPU adds the two stored values exactly, then rounds once to the nearest double. For 0.1 + 0.2 the exact sum falls exactly halfway between two doubles, and the tie is broken toward the neighbour whose last bit is 0. Try other pairs.
Input: two finite decimals separated by ;, each between -1e150 and 1e150 (for example 0.1 ; 0.2 or 1e16 ; 1).
Integers also lose exactness when they become doubles: a double keeps 53 significant bits.
Input: a whole number from -9223372036854775808 to 9223372036854775807 (try 9007199254740993, 9007199254740992, 123).
== after arithmetic. Use a tolerance: (a - b).abs() < 1e-9 (absolute) or relative to the size of the numbers. Never use a double for money: store integer cents, or use BigInt or a decimal package. Input size → feasible: adding 106 doubles accumulates rounding error around 10-10 relative; sum small numbers first, or use Kahan summation, when it matters.double.epsilon constant); every integer up to 253 is a double; double.maxFinite is about 1.8×10308; double.minPositive is a subnormal (exponent field zero). IEEE arithmetic is identical on the VM and on the web.4. Rounding and dividing: round, floor, toStringAsFixed, ~/, %
Imagine standing at a position x on a number line of whole-number tiles. floor steps down to the tile below, ceil steps up, truncate steps toward zero, round goes to the nearest tile (and at the exact middle goes away from zero). They differ only for fractions and negative numbers.
Input: a finite number between -1e15 and 1e15 (try -2.5, 2.5, 0.49999999999999994, 7).
toStringAsFixed(n) rounds the exact binary value, not the digits you typed. That is why 1.005.toStringAsFixed(2) is 1.00. The animation expands the stored value and makes the decision digit by digit.
Input: number ; digits with |number| below 1e21 and digits from 0 to 20 (try 1.005 ; 2, 0.125 ; 2, 2.675 ; 2).
Dividing: ~/ truncates toward zero, remainder matches it, and % never returns a negative number.
Input: a ; b, |a| up to 1015, |b| from 1 to 109 (try -7 ; 3, 7 ; -3).
(i % n), never i.remainder(n): -1 % 5 is 4 but (-1).remainder(5) is -1 and indexes out of range. Input size → feasible: all of these are O(1); doing 108 of them takes about a second.5. Every member, grouped by type
Each card shows the signature, what it means in plain words, a badge, the cost and why, the exceptions, and a runnable example with its exact output. Some cards cover several members that are one-line variations of each other (the card lists them all); every member still appears with its own line of code in the example. Numbers are immutable, so almost every badge says "returns new" or "read-only"; the only mutating members are the Random draws (they advance the generator).
| Badge | Meaning |
|---|---|
| mutates | changes this object (here: advances a Random) |
| returns new | computes and returns a new value; the receiver is untouched (numbers cannot change) |
| read-only | reports a property or constant; creates nothing |
| creates | a constructor, parser or factory that makes a value |
num, 31 of int, 26 of double, 9 of bool, 42 of BigInt, 2 of Comparable, 1 the Comparator typedef, 21 dart:math (8 constants and 13 functions), 5 of Random, 2 inherited Object members (constructors, factories, static helpers, getters, operators and methods, including the overrides that narrow a return type). Deprecated: none of these members is annotated @Deprecated in Dart 3.11; the only historical item is the onError parameter of num.parse (it is marked deprecated and not shown). Not covered here: Point, Rectangle and MutableRectangle from dart:math (geometry classes, not number methods), and Duration / DateTime (lesson D29).Index of every member (click a name to jump to its card)
5.1 num: what int and double share
num is the sealed parent of int and double (sealed: only those two exist and you cannot extend it). Every member below works on both; where int or double narrows the return type (for example int.abs() returns an int) the card names all three. A num variable can hold either, which is handy for a function that should accept both.
Special doubles and the total order in action (every line was run):
Sorting with compareTo (an insertion sort whose every comparison is shown; try your own values, including nan, -0.0, inf):
Input: 2 to 8 values separated by commas; each is a whole number, a decimal (2.5, -0.0), nan, inf or -inf.
clamp step by step (it uses the same total order, so NaN behaves in a surprising way):
Input: x ; lo ; hi, each a number as above (try 15 ; 0 ; 10, nan ; 1 ; 3, 5 ; 5 ; 3).
5.2 int
Members that exist only on int: the bit operators, shifts, bitLength, toSigned / toUnsigned, isEven / isOdd, gcd, modPow, modInverse, toRadixString, int.parse / tryParse and int.fromEnvironment. The bit operators were animated in section 2; below are the three number-theory members, each as the loop it really is.
gcd: Euclid's algorithm
Idea: the greatest common divisor of a and b equals the gcd of b and a % b, and the numbers shrink fast. Input size → feasible: Euclid needs about 5 × (decimal digits) rounds, so even numbers near 1018 take under 100 steps: gcd is never a bottleneck.
Input: a ; b, each between -1015 and 1015 (try 252 ; 105, 17 ; 5, 0 ; 9).
modPow: square and multiply
Idea: write the exponent in binary; for every 1-bit multiply the answer by the current power, and square the power for the next bit. 13 = 11012 needs 4 squarings, not 12 multiplications. Input size → feasible: an exponent of 1018 needs about 60 rounds; keep the modulus below 3×109 in an int (the product of two residues must fit 64 bits), otherwise use BigInt.modPow.
Input: base ; exponent ; modulus with base 0..109, exponent 0..109, modulus 1..2147483647.
modInverse: the extended Euclidean algorithm
Idea: run Euclid on (a, m) and track, for each remainder, how many copies of a it is made of. When the remainder reaches 1, that count is the inverse. If the gcd is not 1, no inverse exists and Dart throws. Input size → feasible: O(log m) steps, so m up to 1018 is instant.
Input: a ; m with a in 0..109 and m in 2..109 (try 3 ; 7, 10 ; 17, 6 ; 9).
Reading numbers from text and writing them in another base
int.parse is Horner's rule: for each character, value = value * radix + digit. toRadixString is the reverse: divide by the radix and collect the remainders. Both are linear in the number of characters.
Input: text ; radix, text of 1 to 12 letters/digits with an optional leading -, radix 2..36 (try ff ; 16, 1010 ; 2, zz ; 36, g ; 16).
Input: n ; radix, |n| up to 1015, radix 2..36 (try 255 ; 16, -255 ; 2).
What int.tryParse accepts and rejects (every line was run):
5.3 double
Besides the five constants, double overrides the arithmetic and rounding members of num to return double (they are on the num cards, as double.+, double.round and so on) and adds parse / tryParse.
5.4 bool
bool is a final class with exactly two instances. Its extra members are small but have traps: the non-short-circuit operators and the strict parser.
5.5 BigInt
A plain int is a pocket calculator with a fixed number of digits. A BigInt is paper and pencil: you can always write one more digit, but every operation needs more work as the numbers get longer (add: one pass; multiply: every digit against every digit).
Growth: factorials compared in an int (which wraps) and a BigInt, with the 32-bit digits the BigInt needs:
Input: n, a whole number from 0 to 40 (n! needs 160 bits at n = 40). Input size → feasible: 1000! (2568 digits) takes a few milliseconds; 100000! takes minutes because each multiplication touches every digit.
Input: steps, a whole number from 0 to 80.
5.6 Comparable and Comparator
The contract behind sorting. The class below is used by the cards and by the animation; it implements Comparable by comparing major, then minor.
Input: 2 to 7 versions such as 1.10, 1.2, 0.9 (major.minor, each up to 3 digits).
5.7 dart:math functions and constants
Import with import 'dart:math';. These are top-level functions, not methods. min and max are plain Dart; the others call the platform's math library. All results are doubles (except min, max and pow, which can return an int).
Square root by Newton's method
Idea: if g is too big, x / g is too small; their average is closer. Each round doubles the number of correct digits. Input size → feasible: 6 to 8 rounds for any double, so it costs O(1) like sqrt; the library sqrt is one CPU instruction and always at least as accurate.
Input: a number between 0.0001 and 1012 (try 2, 144, 0.5).
Integer power by squaring (and where it overflows)
This is the actual loop inside pow for an int base and non-negative int exponent, copied from the SDK. Input size → feasible: O(log exponent) multiplications, but the answer must fit 64 bits: 340 still fits, 341 already wrapped.
Input: base ; exponent, base -1000..1000, exponent 0..100 (try 3 ; 13, 3 ; 41, 2 ; 63).
Digits of a number, done wrong and right, and trigonometry's rounding noise, and min / max edge cases (all lines were run):
5.8 Random
A seeded Random is a very long, shuffled list of numbers that the seed decides the starting point in. Every call reads the next number from the list. Same seed, same starting point, same numbers. Random.secure() is different: it asks the operating system for unpredictable bits, so there is no list to replay.
The seeded generator, step by step. The animation runs a line-by-line port of the VM's generator (a 64-bit multiply-with-carry), which is compared in verify/d23.dart with the real Random on hundreds of seeds. Draw nextInt values for your own seed:
Input: seed ; max ; count, seed any int, max 1..4294967296, count 1..6 (try 42 ; 100 ; 4).
nextDouble:
Input: a seed, any whole number.
Rolling a die many times (nextInt(6) is uniform):
Input: seed ; sides ; rolls, sides 2..12, rolls 1..5000.
Random for passwords, tokens or keys: from a few outputs the whole stream can be predicted. Use Random.secure(). And never create a new Random(42) inside a loop: every iteration would replay the same first number; create it once and keep it.6. Mutable vs immutable: numbers, bool, BigInt, Random
Numbers, bool and BigInt are immutable values: there is no member that changes one in place. x += 1 does not change the number 5; it makes a new number 6 and points x at it. Literals are canonicalised (one shared object per value in a program), which is why identical(2.5, 2.5) is true and why const works with them. Random is the one mutable type in this lesson: each draw changes its hidden state.
| Type | Can change after creation? | const possible? | == means | Pitfall |
|---|---|---|---|---|
int, double | never | yes (all literals) | equal value (NaN is never equal; 0.0 == -0.0) | an int field is not "shared state": copying copies the value |
bool | never | yes | same object | no implicit truthiness: if (1) is an error |
BigInt | never | no (no const constructor; use BigInt.parse) | equal value, only with another BigInt | each operation allocates a new digit list (cost O(n)) |
Random | yes: every draw advances its state | no | identity (two Random(1) are different objects) | sharing one generator across tests makes results depend on call order |
List<int>), not to the numbers themselves.7. Native versus web
Dart compiles to machine code (native: the VM, AOT) or to JavaScript (web: dart2js). JavaScript has one number type, a 64-bit double, and Dart's int becomes that. The table below was measured: the same expression was run on the native VM (Dart 3.11.5) and compiled with dart compile js and run in Node v22.22.2.
Summary of the rules. Native: int is exactly 64-bit two's complement, bit operators use all 64 bits, literals up to 0x7FFFFFFFFFFFFFFF, BigInt uses 32-bit digits, seeded Random is the generator shown above. Web: int is exact only up to 253 and literals beyond that do not compile; bitwise operators and shifts work on 32 bits; 1.0 and 1 are the same value (identical(1, 1.0), 3.0 is int are true, and 1.0.toString() prints 1); BigInt uses 16-bit digits; toInt and pow do not wrap. If your code must behave the same everywhere, keep numbers below 253 or use BigInt, avoid relying on 64-bit shifts, and compare with == only on values you know are exact. Double arithmetic, 0.1 + 0.2 and the rounding methods are identical on both.
8. Choosing int, double, num or BigInt
| You need… | Use | Why |
|---|---|---|
| counts, indexes, ids, money in cents | int | exact, one instruction; stay below 9.2×1018 (and below 253 if the code may run on the web) |
| measurements, averages, physics, percentages | double | wide range; accept rounding; compare with a tolerance |
| a function that takes either | num | the common parent; clamp, abs, round work on both |
| factorials, Fibonacci(1000), RSA, exact huge sums | BigInt | never overflows; cost grows with the digits |
| modular arithmetic with a modulus below 3×109 | int.modPow and friends | fits 64 bits; fast |
| a flag | bool | no truthiness: write the comparison |
| reproducible test data, shuffles | Random(seed) | same seed, same sequence |
| tokens, keys, anything an attacker must not guess | Random.secure() | OS cryptographic source |
| Operation | int | double | BigInt |
|---|---|---|---|
+ - * | O(1), wraps on overflow | O(1), rounds | O(n), O(n), O(n·m) |
| divide | O(1), ~/ throws on 0 | O(1), 0 gives Infinity/NaN | O(n·m), throws on 0 |
| compare | O(1) | O(1), NaN is unordered | O(1) to O(n) |
toString | O(digits ≤ 20) | O(1) (≤ 17 digits) | O(n2) |
| bit operations | O(1), 64 bits | none | O(n), unlimited |
| memory | 0 bytes extra (in the slot) | 8 bytes | object + 4 bytes per 32 bits |
Rule of thumb: default to int for things you count and double for things you measure; switch to BigInt the moment a result might pass 263; and never let a double near money or an equality test.
Quiz
Interview questions
Cheat sheet: every member
Generated from the same member data as the cards above: every member with its cost and whether it returns new, reads or mutates.