A guide to JavaScript calculations, numerical libraries, floating-point limits, and exact fractions.

Math, JavaScript, and Choosing a Numerical Library

Start with built-in Math in JavaScript when ordinary numeric operations meet your requirements. Add a numerical library when a specific capability, such as fraction arithmetic, makes the extra installation and maintenance worthwhile.

Use the built-in operations first

Arithmetic operators handle addition, subtraction, multiplication, division, and exponentiation. The Math object adds static methods and constants; it is not a package and does not need to be imported or constructed.

const subtotal = 19.95;
const quantity = 3;
const total = subtotal * quantity;       // 59.849999999999994
const rounded = Math.round(total);       // 60
const distance = Math.hypot(3, 4);       // 5
const largest = Math.max(4, 9, 2);       // 9

Math.round rounds to the nearest integer; exact half increments go toward positive infinity. Math.hypot calculates the square root of the sum of squares, and Math.max returns the largest argument. These methods are useful building blocks, but none defines how your application should round currency or validate input. Set those rules explicitly.

The distinction matters when reviewing math, JavaScript, and dependency choices together. A package can offer a richer API, but a function name alone does not establish the precision or behavior your application needs. Check its data types and supported operations before adding it to a project.

Understand floating-point limits

JavaScript calculations with the Number type use double-precision binary floating point. Many decimal fractions have no exact representation in that format, so a familiar calculation can produce a surprising result:

0.1 + 0.2 === 0.3; // false
0.1 + 0.2;         // 0.30000000000000004

Formatting the result with toFixed can be useful for display, but it returns a string and does not change the representation used during earlier calculations. For approximate comparisons, select a tolerance suited to the scale and error budget of the calculation. Number.EPSILON describes spacing near 1; it is not a universal threshold for every magnitude.

Test values just below, at, and just above each rounding boundary, including negative values. Include zero, very large magnitudes, and non-finite results where inputs may produce them. Compare each result with the expected business rule, not merely with a snapshot of the current output. This exposes assumptions before they reach invoices or reports.

For money, integer minor units can be practical if the inputs, rounding rules, and range are controlled. Integers are only safe for exact integer operations within the safe integer range; check Number.isSafeInteger where values might grow. BigInt supports larger integers, but built-in Math methods do not accept it. Neither approach automatically supplies a rounding policy for division.

JavaScript math library: Work with fractions

A JavaScript math library such as math.js can retain fractions through supported operations. Install it with npm install mathjs, then use named imports in an ES module:

import { add, fraction } from 'mathjs';

const first = fraction('1/3');
const second = fraction('1/6');
const result = add(first, second);

console.log(result.toFraction()); // "1/2"

Here the inputs describe rational values rather than approximations entered as decimal numbers. The result remains a fraction for this operation. That is useful when a recipe, measurement, or ratio must stay in fractional form through a calculation. It does not mean every operation in the package preserves fractions: its documentation notes that unsupported functions can convert them to numbers.

Before adopting a math library, test representative inputs, output types, and edge cases against your own requirements. A JavaScript library also needs to fit your deployment environment. Check the package’s documentation, compatibility, update practices, and dependency cost. A larger toolbox is only an advantage if the application uses it reliably.

Use built-ins for straightforward operations when their numeric behavior and error bounds are acceptable. Choose a specialized library when a required data type or operation materially improves the result and you are prepared to maintain the dependency.