Learn by reading through in order

Numbers and Arithmetic — the Number Type and Math

Learn the Number type: remainder and exponents, rounding with Math, aligning digits with toFixed, and decimal errors.

A lot of what a web app calculates comes down to numbers — an order subtotal, sales tax, shipping, points earned. JavaScript has no separate types for integers and decimals; it handles both with the same Number type.

This article covers the arithmetic operators, including remainder and exponents, rounding with Math, aligning digits with toFixed, and the decimal precision errors that trip people up in money calculations.

One Number Type — Number and Arithmetic Operators

Some languages split integers and decimals into separate types. This notion of "kind of value" is called a type (the classification of a value — whether it's a number, text, and so on).

JavaScript makes no distinction between integers and decimals — 1200 and 0.08 are both the same Number type (the type that represents numbers). You never choose a type when declaring a variable, but the tradeoff is that dividing two integers can leave a decimal result.

Symbols that direct a calculation are called arithmetic operators (symbols that represent a calculation between values). Beyond addition +, subtraction -, multiplication *, and division /, there's % for the remainder of a division and ** for exponents.

Arithmetic Operators and Their Results
a + badda - bsubtracta * bmultiplya / bdividea % bremaindera ** bexponent7 / 2→ 3.57 % 2→ 12 ** 10→ 1024
The top 2 rows are the operators; the bottom row is what they actually produce. Division leaves a decimal, % returns the remainder, and ** returns the power.
console.log(1200 + 300);   // 1500
console.log(1200 - 300);   // 900
console.log(1200 * 3);     // 3600

// Even with two integers, a leftover decimal remains if it doesn't divide evenly
console.log(7 / 2);        // 3.5

// % is the remainder, ** is exponentiation
console.log(7 % 2);        // 1
console.log(2 ** 10);      // 1024

% is handy for finding a leftover amount, or "the remainder when split into groups of a fixed size." For a total of 4380, 4380 % 100 gives you 80 — the amount left over once you count in units of 100.

** gives you exponents, letting you calculate things like 2 ** 10 — the number of bytes in a kilobyte.

Get the total and average from a day's 3 orders. Each order's amount — orderA, orderB, and orderC — is already declared.

① Display the total of all 3 orders.

② Display the average amount per order.

③ Display the leftover once the total is counted in units of 100.

(If it runs correctly, an explanation will appear.)

JavaScript / TypeScript Editor

Run code to see output

Rounding with Math — Floor, Ceiling, and Nearest

You can't put the average, 1903.3333333333333, straight on an invoice. Amounts and counts need rounding to a whole number somewhere, and whether you round down, up, or to the nearest value depends on business rules.

JavaScript groups these rounding functions under Math.

Let's introduce the term function first (a named unit that runs a fixed process on a value you pass it, and returns a result). Getting a result back from a value you passed in is described as "calling" it and having it "return" a value.

A function under Math (a built-in JavaScript collection of numeric functions) is called by writing Math, a dot, and then the function name — like Math.floor(1903.33).

Math.floor rounds a decimal down, Math.ceil rounds it up, and Math.round rounds to the nearest whole number. All three just return a new number — the variable you passed in stays the same.

3 Rounding Functions, 3 Different Results
Math.floor(149.8)Rounds thedecimal down149Math.ceil(149.8)Rounds thedecimal up150Math.round(149.8)Rounds to thenearest whole150
Pass the same 149.8 to each, and floor returns 149, while ceiling and round both return 150.
const points = 149.8;             // Points calculated from the purchase amount

console.log(Math.floor(points));  // 149  round down
console.log(Math.ceil(points));   // 150  round up
console.log(Math.round(points));  // 150  round to nearest

console.log(points);              // 149.8  the original value doesn't change

// max returns whichever value you pass it is larger
console.log(Math.max(0, 5000 - 5200));   // 0

Another one you'll reach for outside of rounding is Math.max. It returns whichever value you pass it is larger, so Math.max(0, -120) is 0.

That gives you a floor for a calculation you don't want dropping below zero — useful for something like "how much more until free shipping."

Find how much more is needed for free shipping, and the points earned, from a cart subtotal. The subtotal cartSubtotal and the free-shipping threshold freeShippingLine are already declared.

① Display how much more is needed for free shipping — don't let it go negative once the subtotal passes the threshold.

② Display the points earned — 3.5% of the subtotal — rounded down to a whole number.

③ Display, rounded up, how many more items priced at 250 would be needed to reach free shipping.

JavaScript / TypeScript Editor

Run code to see output

Aligning Digits with toFixed — It Returns a String

Sometimes, like with an average amount, you want to decide the number of digits shown up front. Displaying 1903.3333333333333 as-is is hard to read, so you clean it up to one decimal place and show 1903.3.

Use toFixed for that.

toFixed is a method (a function you call by writing a dot after a value) — you call it with a dot after a number. A value passed inside the parentheses is called an argument (a value passed inside a function's or method's parentheses). Pass it the number of decimal places to keep, as in toFixed(1), and it rounds at the next digit down and returns the result kept to that many places. But what comes back is a string, not a number. Use it only right before displaying a value — for further calculation, use the original number or Math.round instead.

Math.floor(value) passes a value to a function that lives inside the Math container; value.toFixed(digits) calls a function the value itself carries. What comes before the dot tells you which form you're looking at.

What toFixed and Math.round Each Return
toFixed(1)Aligns digits,returns a string"1903.3"toFixed(0)Aligns digits,returns a string"1903"Math.round(x * 10)/ 10Returns therounded number1903.3
Both can look like the same 1903.3, but toFixed returns a string, while Math.round returns a number.
const averageOrder = 1903.3333333333333;   // Average amount per order

console.log(averageOrder.toFixed(1));  // 1903.3  returned as a string
console.log(averageOrder.toFixed(0));  // 1903    returned as a string

// To keep calculating, round while staying a number
console.log(Math.round(averageOrder * 10) / 10);  // 1903.3  still a number

toFixed's Result Is for Display Only

Since what toFixed returns is a string, adding or multiplying that result behaves differently than it would with a number. The difference between strings and numbers, and how to check which one you have, are covered in the type conversion article.

Format a sale item's discount rate into something you can display. The list price listPrice and sale price salePrice are already declared.

① Display the given discount rate, discountRate, unrounded.

② Display the same rate, aligned to one decimal place.

③ Display the same rate, aligned with no decimal places at all.

JavaScript / TypeScript Editor

Run code to see output

Decimal Precision Error — Why 0.1 + 0.2 Isn't 0.3

Chain enough calculations involving decimals, and the answer can drift slightly off. This is what causes bugs like a total that's off by a cent, so let's look at why it happens and how to avoid it before it bites you.

Computers store numbers in binary. 0.1 in decimal doesn't divide evenly in binary — it becomes an infinitely repeating fraction — so it gets stored as a different, very close value instead.

0.1 and 0.2 are both stored slightly off, so adding them lands slightly off from 0.3 too.

What Happens When a Decimal Is Stored in Binary
0.1Doesn't divideevenly in binaryStored as a valueclose to 0.10.2Doesn't divideevenly in binaryStored as a valueclose to 0.20.1 + 0.2Adds twoclose valuesSlightly morethan 0.3
Both 0.1 and 0.2 are already slightly off the moment they're stored, so the sum drifts away from 0.3 too.
console.log(0.1 + 0.2);   // 0.30000000000000004

// Scale up to integers first, calculate, then scale back down — no drift
console.log((0.1 * 10 + 0.2 * 10) / 10);   // 0.3

Store Amounts as Whole Numbers

The more decimal additions you chain, the more the drift compounds. For amounts of money, store them as whole-number units and only divide right before display — that sidesteps this error entirely.

Add up product weights for a shipping calculation. Two weights, weightA and weightB, are already declared.

① Display the result of adding the two weights directly.

② Display that same total, rounded to one decimal place as a number.

③ Scale both weights up by 10 to whole numbers, add them, then divide by 10, and display the result.

JavaScript / TypeScript Editor

Run code to see output

Values from Failed Calculations — NaN and Infinity

When a calculation doesn't work out, JavaScript doesn't stop — it returns a special value and keeps going. A lot of bugs where a total shows up blank or an amount displays wrong come from this kind of value getting mixed into a calculation that just kept running.

A result that can't be expressed as a number is NaN (short for Not a Number — a value meaning the result couldn't become a number). 0 / 0, from trying to average a day with zero orders, is one example.

Dividing by zero the other way, as in 5710 / 0, returns Infinity (a value representing infinity). Neither stops with an error, so you might not notice until it shows up on screen.

const orderTotal = 5710;
const orderCount = 0;                  // There were 0 orders that day

console.log(orderTotal / orderCount);  // Infinity
console.log(0 / 0);                    // NaN

// Once NaN gets mixed in, every calculation after it becomes NaN too
console.log(0 / 0 + 1000);             // NaN

Just like Math, Number groups together its own set of number-related functions. You can check whether a value is a whole number with Number.isInteger. Number.isInteger(3) returns true and Number.isInteger(2.5) returns false — a boolean (a value representing one of two choices: whether something holds true) — so it's useful for confirming a value like a count that shouldn't allow decimals.

How to check whether two values are equal is covered in the next article, but NaN is the one exception — it doesn't even equal itself using that method.

So checking for NaN uses a dedicated check instead. That's covered, along with converting strings to numbers, in the type conversion article.

console.log(Number.isInteger(3));      // true
console.log(Number.isInteger(2.5));    // false
console.log(Number.isInteger(6 / 2));  // true   an even division is a whole number

NaN and Infinity flow into the next calculation without ever stopping on an error. When a total or average displays wrong, suspect a division by zero first — it's usually the fastest way to find the cause.

QUIZ

Knowledge Check

Answer each question one by one.

Q1What happens when you run console.log(10 / 4);?

Q2What does calling toFixed(2) on a number return?

Q3What does console.log(0.1 + 0.2); display?