JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
20: Number Methods and Precision Issues
A few years ago, I was reviewing a pull request for a junior dev who was building a simple shopping cart. He had a bug where a customer's total—exactly $20.00—was being rejected by the payment gateway because the internal value was actually 19.999999999999996. He spent an entire afternoon hunting for a "leak" in the logic, convinced he had a rounding error in his tax calculation. In reality, he had just run into the fundamental way JavaScript (and most other languages) handles numbers. It's a rite of passage for every JS developer, but it's one that can cost you real money if you aren't careful.
The Floating Point Headache
JavaScript doesn't have separate types for integers and decimals; it treats everything as a 64-bit floating-point number (following the IEEE 754 standard). In plain English: JS stores numbers in binary. While integers are easy for computers, fractions are a nightmare. Some decimals, like 0.1 or 0.2, cannot be represented exactly in binary—they become infinite repeating fractions, similar to how 1/3 becomes 0.333... in base 10.
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
I'll be honest: it feels broken. But it's not a bug in JavaScript; it's just how hardware works. When you're building a financial app or anything where precision is non-negotiable, you can't trust simple addition. The industry standard workaround? Stop using decimals for calculations. Multiply your values by 100 to work in cents as integers, then divide by 100 only when you need to display the final string to the user.
Cleaning Up the Output
Since we can't always avoid these floating-point quirks, we need ways to format the results so the user doesn't see a string of fifteen zeros. This is where toFixed() and toPrecision() come in. Now, a huge warning here: both of these methods return a string, not a number. If you try to add another number to the result of toFixed(), you'll end up with string concatenation, which is a great way to turn "10.50" + 5 into "10.505".
const price = 19.99999;
console.log(price.toFixed(2)); // "20.00" (rounded and converted to string)
const pi = 3.14159;
console.log(pi.toPrecision(3)); // "3.14" (total significant digits)
I usually reach for toFixed(2) for currency, but remember to wrap it in Number() or use the unary plus operator + if you need to turn that result back into a mathematical value for further calculations.
Parsing and Safe Conversion
You've likely used parseInt() and parseFloat(), but there's a nuance I want you to remember. parseInt() is aggressive; it strips everything after the decimal point without rounding. If you have "10.99", parseInt gives you 10. parseFloat() keeps the decimals, but it's still subject to the precision issues we just talked about.
const input = "42.50px";
console.log(parseInt(input)); // 42
console.log(parseFloat(input)); // 42.5
If you are dealing with a string that you know should be a clean number, I prefer using the Number() constructor. It's stricter. While parseFloat("12.5abc") will happily return 12.5, Number("12.5abc") will return NaN. In my experience, it's better to have your code fail loudly with NaN than to silently process a corrupted number and wonder why your database is full of weird values.
📋 Practical Task
The Precision-Proof Invoice Generator
You are building a checkout module for a digital store. You have an array of item prices: [10.10, 20.20, 30.30]. If you sum these normally, you'll get 60.60000000000001.
Your task: Write a function calculateTotal(prices) that:
- Takes an array of floating-point numbers.
- Calculates the sum by converting the numbers to integers (cents) first to avoid precision errors.
- Returns the final total as a string formatted to exactly two decimal places (e.g., "60.60").
- Includes a check: if the array is empty, return "0.00".
Test your function with: calculateTotal([10.10, 20.20, 30.30]). It should return exactly "60.60".
There are no comments for now.