Skip to content
JavaScript Basics: Calculate Exact Integers with BigInt

JavaScript Basics: Calculate Exact Integers with BigInt

October 17, 2026

A counter can grow beyond the range where ordinary JavaScript numbers represent every integer exactly. This tutorial uses BigInt to increase a large counter, divide whole quantities, and save an integer as JSON text.

All examples use built-in features and were tested on Linux with Node.js v24.19.0. Assertions stop the program if a result differs from the expected value. A successful run prints the output shown below.

Prepare a scratch directory

You need Bash, a text editor, the mktemp, rm, and rmdir utilities, and Node.js on your PATH. Save source files as UTF-8; their .mjs extension selects ES modules. Run these commands in Bash and keep using the same terminal:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Create each named file with your editor. No package installation is needed.

1. Increase a large counter

Save this as large-counter.mjs:

import assert from 'node:assert/strict';

const previousCount = BigInt('9007199254740993');
const nextCount = previousCount + 1n;

assert.equal(typeof previousCount, 'bigint');
assert.equal(nextCount, 9007199254740994n);
assert.equal(previousCount, 9007199254740993n);
assert.throws(() => previousCount + 1, TypeError);
assert.equal(1n === 1, false);

console.log(`Previous count: ${previousCount}`);
console.log(`Next count: ${nextCount}`);
console.log(`Value type: ${typeof nextCount}`);

Run it:

node large-counter.mjs

Output:

Previous count: 9007199254740993
Next count: 9007199254740994
Value type: bigint

Create a BigInt with an integer literal ending in n, or convert an integer string with BigInt(). Passing an already-rounded Number cannot recover lost digits. Arithmetic such as addition requires matching numeric types: use 1n, rather than 1. Strict equality also distinguishes those types. Template strings display the digits without the source-code n suffix.

2. Divide into whole groups

Save this as whole-groups.mjs:

import assert from 'node:assert/strict';

const itemCount = 23n;
const groupSize = 5n;
const completeGroups = itemCount / groupSize;
const leftoverItems = itemCount % groupSize;

assert.equal(completeGroups, 4n);
assert.equal(leftoverItems, 3n);
assert.equal(completeGroups * groupSize + leftoverItems, itemCount);
assert.equal(-23n / 5n, -4n);
assert.equal(-23n % 5n, -3n);
assert.throws(() => itemCount / 0n, RangeError);

console.log(`Complete groups: ${completeGroups}`);
console.log(`Leftover items: ${leftoverItems}`);
console.log(`Negative division: ${-23n / 5n}`);

Run it:

node whole-groups.mjs

Output:

Complete groups: 4
Leftover items: 3
Negative division: -4

BigInt stores integers, not fractions. Division truncates toward zero; % gives a remainder, which can be negative. Dividing by zero throws. BigInt also cannot be passed directly to Math functions. Choose it for whole-number arithmetic, not as a drop-in decimal type.

3. Make a JSON representation explicit

Save this as counter-json.mjs:

import assert from 'node:assert/strict';

const counter = { count: 9007199254740993n };
assert.throws(() => JSON.stringify(counter), TypeError);

const savedText = JSON.stringify({ count: counter.count.toString() });
const parsedRecord = JSON.parse(savedText);
const restoredCount = BigInt(parsedRecord.count);

assert.equal(typeof parsedRecord.count, 'string');
assert.equal(restoredCount, counter.count);

console.log(savedText);
console.log(`Restored count: ${restoredCount}`);
console.log(`Restored type: ${typeof restoredCount}`);

Run it:

node counter-json.mjs

Output:

{"count":"9007199254740993"}
Restored count: 9007199254740993
Restored type: bigint

JSON.stringify() rejects BigInt values by default. This example defines count as a decimal string in the saved format, then explicitly restores that known field. JSON does not remember the original BigInt type. Do not automatically convert every numeric-looking string: a label or identifier might need its original spelling. External data also needs validation before conversion. Converting a large BigInt to Number just to serialize it can lose precision.

Clean up

In the same terminal, remove only the files created here, return to your original directory, and remove the now-empty scratch directory:

rm -- large-counter.mjs whole-groups.mjs counter-json.mjs
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

Acknowledgements

This post was created with assistance from AI.

Last updated on