{"id":"l-js-1-3","courseId":"javascript-fundamentals","lang":"en","kind":"lesson","summary":"Do arithmetic with JavaScript numbers, avoid floating-point surprises by storing money in whole cents, and compare values safely with === instead of ==.","html":"<p>Ask JavaScript to add up a coffee, a lunch and a bus ticket, and it gets the answer slightly wrong:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"0\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4.1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 12.2</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2.7</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>You expected 19 and got <code dir=\"ltr\">18.999999999999996</code>. This is not a JavaScript bug; nearly every programming language does the same thing. Once you know why, you can design Pocket so it never bites you.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Arithmetic, the usual way</h2>\n<p>The arithmetic operators work as you would hope, with the usual precedence: <code dir=\"ltr\">*</code>, <code dir=\"ltr\">/</code> and <code dir=\"ltr\">%</code> happen before <code dir=\"ltr\">+</code> and <code dir=\"ltr\">-</code>, and parentheses override everything.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"1\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> *</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);    </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// multiplication first</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">*</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// parentheses first</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">17</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> %</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);       </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// remainder after dividing 17 by 5</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> **</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 10</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);      </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 2 to the power of 10</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p><code dir=\"ltr\">%</code> (remainder) is more useful than it looks: <code dir=\"ltr\">n % 2 === 0</code> tells you whether a number is even, and <code dir=\"ltr\">minutes % 60</code> gives the leftover minutes after whole hours.</p>\n<p>When you update a variable from its own value, there are shorthands. <code dir=\"ltr\">total += 450</code> means <code dir=\"ltr\">total = total + 450</code>, and the same pattern works for <code dir=\"ltr\">-=</code>, <code dir=\"ltr\">*=</code> and <code dir=\"ltr\">/=</code>. <code dir=\"ltr\">count++</code> adds one, which you will see in loops.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Why 0.1 + 0.2 is not 0.3</h2>\n<p>JavaScript has a single number type, used for both whole numbers and decimals. Under the hood every number is stored in binary, in 64 bits, following a standard called IEEE 754. Whole numbers fit perfectly. Many decimals do not: in binary, 0.1 is a fraction that repeats forever, like 1/3 in decimal (0.3333…). The computer has to cut it off somewhere, so it stores the nearest value it can. Add a few of these near-misses and the error becomes visible.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"2\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.2</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ===</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0.3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4.1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> *</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>The error is tiny, about one part in ten quadrillion, so for measuring distances or drawing on screen it never matters. For money it does. A total that should be 19 but is stored as 18.999999999999996 fails an equality check, rounds the wrong way on a receipt, and drifts further every time you add another expense. Rounding the display hides the symptom while the stored number stays wrong.</p>\n<p>For money, the fix is simple and it is what payment companies do: <strong>store amounts as whole cents</strong>. Whole numbers are exact up to <code dir=\"ltr\">Number.MAX_SAFE_INTEGER</code>, about 9 quadrillion, which is plenty for an expense tracker.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"3\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> coffee</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 410</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// $4.10 in cents</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> lunch</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> bus</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 270</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> total</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> coffee </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> lunch </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> bus;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(total);         </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// exact</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(total </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">/</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// convert to dollars only when you display it</span></span></code></pre><div class=\"run-output\" data-run-output=\"3\" hidden></div></div>\n<p>From this lesson on, every amount in Pocket is stored in cents. When an amount arrives in dollars, such as <code dir=\"ltr\">4.1</code>, convert it once with <code dir=\"ltr\">Math.round(4.1 * 100)</code>. The <code dir=\"ltr\">Math.round</code> matters, because <code dir=\"ltr\">4.1 * 100</code> is <code dir=\"ltr\">409.99999999999994</code>, and rounding snaps it to <code dir=\"ltr\">410</code>.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">When + joins instead of adds</h2>\n<p>Values typed into a web page always arrive as strings, even when they look like numbers. And <code dir=\"ltr\">+</code> has two jobs: adding numbers and joining strings. If either side is a string, it joins.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"4\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> typed</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"5\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(typed </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);          </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// joins: \"53\"</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(typed </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);          </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// only maths makes sense, so 2</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">Number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(typed) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// convert first: 8</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">Number</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"12abc\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">));    </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// not a number: NaN</span></span></code></pre><div class=\"run-output\" data-run-output=\"4\" hidden></div></div>\n<p>The other operators (<code dir=\"ltr\">-</code>, <code dir=\"ltr\">*</code>, <code dir=\"ltr\">/</code>) only do maths, so they convert strings to numbers silently, which makes the bug harder to spot: the subtraction works and the addition does not. Convert input explicitly with <code dir=\"ltr\">Number()</code> as soon as you receive it.</p>\n<p>When a conversion fails you get <code dir=\"ltr\">NaN</code>, “not a number”, which is confusingly of type number. <code dir=\"ltr\">NaN</code> spreads: any maths with it gives <code dir=\"ltr\">NaN</code>. Test for it with <code dir=\"ltr\">Number.isNaN(value)</code>, because <code dir=\"ltr\">NaN === NaN</code> is <code dir=\"ltr\">false</code>.</p>\n<p>Its cousin is <code dir=\"ltr\">Infinity</code>. Dividing by zero does not crash a JavaScript program the way it does a calculator: <code dir=\"ltr\">5 / 0</code> is <code dir=\"ltr\">Infinity</code> and <code dir=\"ltr\">-5 / 0</code> is <code dir=\"ltr\">-Infinity</code>. That sounds harmless until Pocket computes “average spend per day” on the first of the month with zero days recorded. Neither <code dir=\"ltr\">NaN</code> nor <code dir=\"ltr\">Infinity</code> throws an error, so your program keeps running and shows nonsense. The defence is the same for both: check your inputs before you calculate.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Adding raw input</span></p>\n<p><code dir=\"ltr\">total = total + amountInput</code> looks right and produces <code dir=\"ltr\">&quot;0450&quot;</code> the first time someone uses your form. Convert with <code dir=\"ltr\">Number(amountInput)</code> first, then check the result with <code dir=\"ltr\">Number.isNaN</code> before using it.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">Comparing values</h2>\n<p>Comparison operators produce a boolean: <code dir=\"ltr\">&gt;</code>, <code dir=\"ltr\">&lt;</code>, <code dir=\"ltr\">&gt;=</code>, <code dir=\"ltr\">&lt;=</code>, and the two kinds of equality.</p>\n<p><code dir=\"ltr\">===</code> (strict equality) is <code dir=\"ltr\">true</code> only when both values have the same type and the same value. <code dir=\"ltr\">==</code> (loose equality) first converts the two sides to a common type, following rules that surprise even experienced developers:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Expression</th>\n<th><code dir=\"ltr\">==</code></th>\n<th><code dir=\"ltr\">===</code></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">&quot;5&quot;</code> and <code dir=\"ltr\">5</code></td>\n<td>true</td>\n<td>false</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&quot;&quot;</code> and <code dir=\"ltr\">0</code></td>\n<td>true</td>\n<td>false</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">null</code> and <code dir=\"ltr\">undefined</code></td>\n<td>true</td>\n<td>false</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&quot;0&quot;</code> and <code dir=\"ltr\">false</code></td>\n<td>true</td>\n<td>false</td>\n</tr>\n</tbody>\n</table></div><p>Use <code dir=\"ltr\">===</code> and <code dir=\"ltr\">!==</code> everywhere. If the types might differ, convert explicitly first, then compare. Your code then says exactly what it means, and nobody has to remember a conversion table.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"5\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> budgetCents</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2500</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> spentCents</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2200</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> isOverBudget</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> spentCents </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> budgetCents;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(isOverBudget, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">typeof</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> isOverBudget);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(spentCents </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2200</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"2200\"</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ===</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2200</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"5\" hidden></div></div>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Rounding for humans</span></p>\n<p><code dir=\"ltr\">Math.round</code>, <code dir=\"ltr\">Math.floor</code> (down) and <code dir=\"ltr\">Math.ceil</code> (up) return whole numbers. <code dir=\"ltr\">Math.max(3, 9, 4)</code> and <code dir=\"ltr\">Math.min(...)</code> pick the largest or smallest. You will use all of them in Pocket.</p>\n</aside>\n<p>With exact amounts and safe comparisons, the next step is to show them nicely: strings, and how to build readable text like “Lunch: $12.20”.</p>\n","toc":[{"id":"sec-1","text":"Arithmetic, the usual way","level":2},{"id":"sec-2","text":"Why 0.1 + 0.2 is not 0.3","level":2},{"id":"sec-3","text":"When + joins instead of adds","level":2},{"id":"sec-4","text":"Comparing values","level":2}],"takeaways":["JavaScript has one number type for whole and decimal numbers, and decimals are stored in binary, so <code dir=\"ltr\">0.1 + 0.2</code> is not exactly <code dir=\"ltr\">0.3</code>.","Store money as whole cents (integers) and convert to dollars only for display; whole-number arithmetic is exact.","<code dir=\"ltr\">+</code> with a string joins text instead of adding, so convert input with <code dir=\"ltr\">Number()</code> before doing maths.","Always compare with <code dir=\"ltr\">===</code> and <code dir=\"ltr\">!==</code>; <code dir=\"ltr\">==</code> converts types first and gives answers like <code dir=\"ltr\">\\&quot;\\&quot; == 0</code> being true."],"further":[{"title":"Expressions and operators (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_operators"},{"title":"Equality comparisons and sameness (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness"},{"title":"Number (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number"}],"quiz":[{"q":"<p>A user types <code dir=\"ltr\">5</code> into a text box, so your code receives the string <code dir=\"ltr\">&quot;5&quot;</code>. What is <code dir=\"ltr\">&quot;5&quot; + 3</code>?</p>","options":[{"text":"<code dir=\"ltr\">8</code>","why":"That is what you wanted, but <code dir=\"ltr\">+</code> sees a string on one side and switches to joining text."},{"text":"<code dir=\"ltr\">&quot;53&quot;</code>","why":"Correct. When either side of <code dir=\"ltr\">+</code> is a string, JavaScript converts the other side to a string and joins them. Convert first with <code dir=\"ltr\">Number(&quot;5&quot;)</code>."},{"text":"<code dir=\"ltr\">NaN</code>","why":"<code dir=\"ltr\">NaN</code> appears when a maths operation cannot produce a number. Here <code dir=\"ltr\">+</code> happily joins text, so no maths fails."},{"text":"A TypeError, because you cannot add a string and a number.","why":"JavaScript never throws here; it converts types silently, which is exactly why this bug is easy to miss."}],"answer":1},{"q":"<p>Pocket stores a coffee as <code dir=\"ltr\">4.1</code> dollars and a lunch as <code dir=\"ltr\">12.2</code>. Why is storing <code dir=\"ltr\">410</code> and <code dir=\"ltr\">1220</code> cents a better design?</p>","options":[{"text":"Integer arithmetic is exact, while binary decimals like 4.1 carry tiny rounding errors that pile up in totals.","why":"Correct. Whole numbers up to about 9 quadrillion are represented exactly, so sums of cents never drift."},{"text":"Whole numbers take less memory than decimals in JavaScript.","why":"Both are the same number type and use the same amount of memory. The benefit is accuracy, not size."},{"text":"JavaScript cannot add decimals at all without a library.","why":"It can add them; the result is just sometimes a hair off, such as <code dir=\"ltr\">18.999999999999996</code> instead of 19."}],"answer":0},{"q":"<p>Which comparison is <code dir=\"ltr\">true</code>?</p>","options":[{"text":"<code dir=\"ltr\">&quot;0&quot; === 0</code>","why":"<code dir=\"ltr\">===</code> never converts types, and a string is never strictly equal to a number."},{"text":"<code dir=\"ltr\">NaN === NaN</code>","why":"NaN is the one value not equal to itself. Use <code dir=\"ltr\">Number.isNaN(value)</code> to test for it."},{"text":"<code dir=\"ltr\">10 % 3 === 1</code>","why":"Correct. <code dir=\"ltr\">%</code> gives the remainder of a division, and 10 divided by 3 leaves a remainder of 1."},{"text":"<code dir=\"ltr\">2 + 3 * 2 === 10</code>","why":"Multiplication runs before addition, so the left side is <code dir=\"ltr\">2 + 6</code>, which is 8."}],"answer":2},{"q":"<p>What does <code dir=\"ltr\">let n = 4; n += 2; n *= 3;</code> leave in <code dir=\"ltr\">n</code>?</p>","options":[{"text":"<code dir=\"ltr\">18</code>","why":"Correct. <code dir=\"ltr\">n += 2</code> makes it 6, and <code dir=\"ltr\">n *= 3</code> makes it 18. Each shorthand reads the current value, does the maths, and reassigns."},{"text":"<code dir=\"ltr\">10</code>","why":"That would be <code dir=\"ltr\">4 + 2 * 3</code>. The two statements run one after the other, so the addition finishes before the multiplication starts."},{"text":"<code dir=\"ltr\">14</code>","why":"That would be <code dir=\"ltr\">4 * 3 + 2</code>. The statements run in the order written: add first, then multiply."}],"answer":0}],"exercise":{"kind":"web","mode":"js","starter":{"js":"// Three expenses, still in dollars\nconst coffee = 4.1;\nconst lunch = 12.2;\nconst bus = 2.7;\n// A fourth amount, typed into a text box, so it arrived as a string\nconst typedAmount = \"3\";\n\nconst budgetCents = 2500;\n\n// Fix this: it should be the exact total in whole cents\nconst totalCents = coffee + lunch + bus + typedAmount;\n\n// Declare isOverBudget and leftCents below\n"},"solution":{"js":"const coffee = 4.1;\nconst lunch = 12.2;\nconst bus = 2.7;\nconst typedAmount = \"3\";\n\nconst budgetCents = 2500;\n\nconst totalCents =\n  Math.round(coffee * 100) +\n  Math.round(lunch * 100) +\n  Math.round(bus * 100) +\n  Math.round(Number(typedAmount) * 100);\n\nconst isOverBudget = totalCents > budgetCents;\nconst leftCents = budgetCents - totalCents;\nconsole.log(totalCents, isOverBudget, leftCents);\n"},"checks":[{"id":"total-number","test":"return typeof totalCents === 'number';"},{"id":"total-exact","test":"return totalCents === 2200;"},{"id":"over-budget","test":"return isOverBudget === false;"},{"id":"left","test":"return leftCents === 300;"}],"title":"Total in cents, exactly","prompt":"<p>The starter adds three dollar amounts and a typed string, and gets a mess. Fix <code dir=\"ltr\">totalCents</code> so it is the <strong>exact total in whole cents</strong> of all four amounts (convert each one to cents with <code dir=\"ltr\">Math.round(amount * 100)</code>, and turn <code dir=\"ltr\">typedAmount</code> into a number first). Then declare <code dir=\"ltr\">isOverBudget</code>, a boolean that is <code dir=\"ltr\">true</code> when <code dir=\"ltr\">totalCents</code> is greater than <code dir=\"ltr\">budgetCents</code>, and <code dir=\"ltr\">leftCents</code>, the budget minus the total.</p>\n","hints":["Log the starter’s <code dir=\"ltr\">totalCents</code> to see the problem: a string, because <code dir=\"ltr\">+</code> met <code dir=\"ltr\">&quot;3&quot;</code> and started joining text.","<code dir=\"ltr\">Number(typedAmount)</code> turns <code dir=\"ltr\">&quot;3&quot;</code> into <code dir=\"ltr\">3</code>. Convert each dollar amount with <code dir=\"ltr\">Math.round(x * 100)</code>.","A comparison is already a boolean: <code dir=\"ltr\">const isOverBudget = totalCents &gt; budgetCents;</code>."],"explanation":"<p>Multiplying by 100 moves each amount into cents, but <code dir=\"ltr\">4.1 * 100</code> is <code dir=\"ltr\">409.99999999999994</code>, so <code dir=\"ltr\">Math.round</code> snaps it to the whole number it should be. <code dir=\"ltr\">typedAmount</code> must go through <code dir=\"ltr\">Number()</code> first; otherwise the first <code dir=\"ltr\">+</code> that meets it joins text instead of adding. Once everything is a whole number, the sum is exact: 410 + 1220 + 270 + 300 = 2200. <code dir=\"ltr\">totalCents &gt; budgetCents</code> evaluates to <code dir=\"ltr\">false</code>, and you store that boolean directly instead of writing an <code dir=\"ltr\">if</code>.</p>\n","checkLabels":{"total-number":"<code dir=\"ltr\">totalCents</code> is a number, not a string","total-exact":"<code dir=\"ltr\">totalCents</code> is exactly 2200","over-budget":"<code dir=\"ltr\">isOverBudget</code> is the boolean <code dir=\"ltr\">false</code>","left":"<code dir=\"ltr\">leftCents</code> is 300"}},"words":757,"readMinutes":4}