Number in JavaScript: precision, limits, and reliable calculations

Last updated on
Number in JavaScript: precision, limits, and reliable calculations

In JavaScript, number looks simple: you add, subtract, multiply, and divide. But once you work with money, percentages, large IDs, form data, or calculations that must be exact, important details appear. Understanding how Number works helps you avoid silent bugs and poor technical decisions.

One type for integers and decimals

JavaScript uses one number type for both integers and decimals:

console.log(typeof 42); // "number"
console.log(typeof 3.14); // "number"
console.log(3 === 3.0); // true

Internally, numbers use the 64-bit IEEE 754 format. This gives JavaScript a large numeric range, but it also introduces precision limits.

The classic problem: 0.1 + 0.2

Decimals cannot always be represented exactly in binary:

console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false

This is not a JavaScript bug. It is a consequence of floating-point numbers. To compare decimals, use a tolerance:

function nearlyEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON;
}

console.log(nearlyEqual(0.1 + 0.2, 0.3)); // true

For money, it is usually better to work in smaller integer units. For example, store cents instead of dollars:

const priceInCents = 1299;
const quantity = 3;
const totalInCents = priceInCents * quantity;

console.log(totalInCents); // 3897

NaN and Infinity

JavaScript includes special numeric values:

console.log(10 / 0); // Infinity
console.log(-10 / 0); // -Infinity
console.log(Number('hello')); // NaN

NaN means “Not a Number”, but its behavior can be confusing:

console.log(NaN === NaN); // false
console.log(Number.isNaN(NaN)); // true

Use Number.isNaN() to check whether a value is really NaN. Avoid the global isNaN() when possible because it converts the value first and can produce surprising results.

Parsing numbers from strings

Input values arrive as text. To convert them:

const age = Number('25');
const price = Number.parseFloat('19.99');
const page = Number.parseInt('12', 10);

Number() requires the entire string to be numeric:

console.log(Number('12px')); // NaN
console.log(Number.parseInt('12px', 10)); // 12

That difference is useful, but it can also be dangerous. If you are validating forms, you usually want to detect invalid input instead of accepting a partial number.

Rounding and formatting

toFixed() returns a string, not a number:

const value = 12.345;
const rounded = value.toFixed(2);

console.log(rounded); // "12.35"
console.log(typeof rounded); // "string"

To display currency or locale-aware values, use Intl.NumberFormat:

const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
});

console.log(formatter.format(1299.5));

Keep calculation and presentation separate: calculate with numbers, display with formatters.

Safe limits

JavaScript can represent very large numbers, but not every large integer is safe:

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.isSafeInteger(9007199254740992)); // false

If an ID comes from a database and exceeds that limit, do not treat it as a number. Store it as a string or use BigInt if you need mathematical operations.

BigInt

BigInt can represent integers larger than the safe integer limit:

const huge = 123456789012345678901234567890n;
console.log(typeof huge); // "bigint"

But you cannot mix number and bigint directly:

const count = 10n;
// count + 1; // TypeError
console.log(count + 1n); // 11n

Use it for large integers, not decimals or money with cents.

Math for common operations

The Math object includes common utilities:

console.log(Math.round(4.6)); // 5
console.log(Math.floor(4.6)); // 4
console.log(Math.ceil(4.1)); // 5
console.log(Math.max(3, 9, 1)); // 9

Math.random() is fine for simple random values, but not for security, tokens, or cryptography. Use crypto.getRandomValues() for that.

Best practices

  • Convert strings to numbers explicitly.
  • Validate with Number.isNaN() and Number.isFinite().
  • Do not compare decimals exactly after floating-point operations.
  • For money, use smaller integer units such as cents.
  • Do not treat huge IDs as number; use string or BigInt.
  • Use Intl.NumberFormat when displaying numbers to users.

Conclusion

Number is enough for most everyday calculations, but it is not magic. Knowing where it loses precision, how to validate input, and when to use BigInt or integer money values helps you write more reliable software. Numeric bugs are often silent, so it is better to prevent them in the design.