AniUI Academy

Numbers and Maths

One number type, the remainder operator you will use more than you expect, and why 0.1 + 0.2 is not 0.3 — with the fixes that keep money correct.

10 min read

Prices, scores, durations, page counts — sooner or later everything becomes a number. JavaScript makes numbers easier than most languages in one way and stranger in another, and it is worth knowing both halves early.

One type for everything

Many languages make you choose between whole numbers and decimals. JavaScript does not. There is a single number type, and it holds both.

const lessons = 5;
const rating = 4.5;
 
typeof lessons; // "number"
typeof rating;  // "number"

That is one less decision to make. The cost shows up at the end of this lesson.

The five operators

Four of them look like school maths:

10 + 3; // 13
10 - 3; // 7
10 * 3; // 30
10 / 3; // 3.3333333333333335

Division always gives you a decimal when it does not divide evenly. There is no separate whole-number division.

The fifth is %, the remainder operator. It gives you what is left over after dividing:

10 % 3; // 1  — three fits into ten three times, with 1 left over
9 % 3;  // 0  — nothing left over

It looks like a curiosity until you notice how many real questions are really remainder questions. Is a number even? Divide by two and check for a leftover:

const isEven = (n) => n % 2 === 0;
 
isEven(4); // true
isEven(7); // false

Want to do something to every third item in a list? Check the position against three:

const position = 6;
position % 3 === 0; // true — this is a third item

Wrapping a value round in a circle is the same idea. Hour 25 on a clock is 25 % 12, which is 1.

Precedence, and why brackets are kind

JavaScript follows the ordering you were taught at school: multiplication and division happen before addition and subtraction.

2 + 3 * 4; // 14, not 20

You can force a different order with brackets, and you should use them freely even when they change nothing:

const price = 199;
const quantity = 3;
const shipping = 40;
 
const total = price * quantity + shipping;   // correct, but you had to think
const clearer = (price * quantity) + shipping; // correct and obvious

The brackets cost you two characters and save the next reader a pause.

Shorthand you will read everywhere

Adding to a variable and storing the result back is so common it has its own form:

let score = 10;
 
score = score + 5; // the long way
score += 5;        // the same thing
score -= 2;
score *= 2;

Adding exactly one has an even shorter form, which you will see in loops constantly:

let count = 0;
count++; // count is now 1
count--; // back to 0

The Math toolbox

Math is a built-in collection of number helpers. You do not create it; it is just there.

Math.round(4.5);  // 5   — nearest, .5 goes up
Math.round(4.4);  // 4
Math.floor(4.9);  // 4   — always down
Math.ceil(4.1);   // 5   — always up
Math.max(3, 9, 1); // 9
Math.min(3, 9, 1); // 1

Math.random() gives a decimal from 0 up to, but never reaching, 1. On its own it is rarely what you want, so it is almost always combined with floor to pick a whole number:

const pick = Math.floor(Math.random() * 5); // 0, 1, 2, 3 or 4

Multiply to set the range, then floor to chop off the decimal.

toFixed, and a classic bug

.toFixed(2) rounds to a fixed number of decimal places, which is exactly what prices need:

const total = 597;
total.toFixed(2); // "597.00"

Look closely at those quote marks. toFixed returns a string, not a number. Keep calculating with it and things go quietly wrong:

const price = (199.5).toFixed(2); // "199.50"
price + 1; // "199.501" — glued, not added

Use toFixed at the last possible moment, when you are about to display something. If you need the number back, wrap it: Number(price).

Text into numbers

Anything typed into a form arrives as text, always, even when it looks like a number. Two tools convert it:

Number("42");     // 42
Number("42.5");   // 42.5
Number("42kg");   // NaN — all or nothing
parseInt("42kg"); // 42 — reads from the front and stops
parseInt("kg42"); // NaN

Number() is stricter and usually what you want. parseInt is useful when a value has a unit stuck to it, like "16px".

Forget the conversion and + betrays you:

const typed = "10";
typed + 5;         // "105" — string glue
Number(typed) + 5; // 15

NaN, the not-a-number number

NaN stands for "not a number", and confusingly its type is number. It is what you get when a calculation has no sensible numeric answer:

Number("hello"); // NaN
0 / 0;           // NaN

Dividing a normal number by zero does not produce it, incidentally — 10 / 0 gives Infinity.

NaN has one genuinely strange property: it is not equal to anything, itself included.

NaN === NaN; // false

So you can never test for it with ===. Use the built-in check:

Number.isNaN(Number("hello")); // true

The 0.1 + 0.2 surprise

Try this and the result will not be what you expect:

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

Nothing is broken. Computers store numbers using only halves, quarters, eighths and so on. Some fractions simply cannot be written exactly that way, in the same way one third cannot be written exactly in decimal — you get 0.3333 and keep going forever. One tenth is one of those awkward fractions, so 0.1 is stored as something very slightly off, and the error shows up when you add.

Two practical responses. For comparisons, round before you compare, or check that the gap is tiny rather than demanding exactness. For money, do not store rupees with decimals at all. Store paise as whole numbers, which are exact, and divide only when you display:

const priceInPaise = 19950;
const display = (priceInPaise / 100).toFixed(2); // "199.50"

Try it yourself

Change the quantity, then add a line that works out the price per item rounded down.

Try it yourself
Loading playground...

What to remember

  • One number type covers whole numbers and decimals.
  • % gives the remainder, and answers "is this even" or "is this every third".
  • .toFixed() returns a string — convert back with Number() before any more maths.
  • Form input is always text; convert with Number() before you calculate.
  • NaN fails every comparison, so test with Number.isNaN().
  • Decimals are stored approximately. Count money in paise and format at the end.

Check yourself

4 questions · pass 3/4 to unlock Making Decisions

up to 50
  1. 1.Which expression checks whether a number is even?

  2. 2.What is the value of (199.5).toFixed(2) + 1?

  3. 3.Which statement about NaN is correct?

  4. 4.What is the safest way to handle money in JavaScript?

4 left to answer