Last active
June 12, 2024 16:23
-
-
Save felipeblassioli/33b8dd464b3a47acf9fa08e2f2e80b41 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Études: Quirks and Implications of Numbers in JavaScript | |
// | |
// Reading: | |
// - [Here is what you need to know about JavaScript’s Number type - Max Koretskyi](https://medium.com/angular-in-depth/javascripts-number-type-8d59199db1b6) | |
// Quotes: | |
// > Why 0.1+0.2 IS NOT equal to 0.3 and 9007199254740992 IS equal to 9007199254740993 | |
// | |
// > According to the ECMAScript standard, there is only one type for numbers and it is the ‘double-precision 64-bit | |
// > binary format IEEE 754 value’. This type is used to store both integers and fractions and is the equivalent of `double` | |
// data type in Java and C. | |
// Some new developers to JavaScript do not realize that and believe that if they use 1 it is stored in 64 bits as: | |
// `0000000000000000000000000000000000000000000000000000000000000001' | |
// while in fact it’s stored as: | |
// | |
// `011111111110000000000000000000000000000000000000000000000000000` | |
// | |
// 1. Precision and Gaps in Large Numbers | |
console.log("Precision and Gaps in Large Numbers:"); | |
const largeNumber1 = 9007199254740991; // Maximum safe integer (2^53 - 1) | |
const largeNumber2 = 9007199254740992; // Maximum safe integer + 1 | |
const largeNumber3 = 9007199254740993; // Unreliable due to precision issues | |
console.log(largeNumber1); // 9007199254740991 | |
console.log(largeNumber2); // 9007199254740992 | |
console.log(largeNumber3); // 9007199254740992 - Precision issue! | |
console.log("Gap Example:"); | |
const largeSum = largeNumber1 + 10; | |
console.log(largeSum); // 9007199254741001 - Notice the gap | |
// Explanation: | |
// JavaScript uses double-precision floating-point format which can't accurately represent all integers beyond 2^53-1. | |
// The Number.MAX_SAFE_INTEGER constant represents the maximum safe integer in JavaScript. | |
// | |
// MAX_SAFE_INTEGER: | |
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER | |
const x = Number.MAX_SAFE_INTEGER + 1; | |
const y = Number.MAX_SAFE_INTEGER + 2; | |
console.log(Number.MAX_SAFE_INTEGER); // Expected output: 9007199254740991 | |
console.log(x); // Expected output: 9007199254740992 | |
console.log(x === y); // Expected output: true | |
// | |
// 2. Comparing Numbers | |
// | |
console.log("\nComparing Numbers:"); | |
const num1 = 0.1 + 0.2; | |
const num2 = 0.3; | |
console.log("num1:", num1); // 0.30000000000000004 | |
console.log("num2:", num2); // 0.3 | |
console.log("Direct comparison (num1 === num2):", num1 === num2); // false | |
// Edge Case: Comparing Floating Point Numbers | |
console.log("Comparing Floating Point Numbers:"); | |
// The value of Number.EPSILON is the difference between 1 and the smallest value greater | |
// than 1 that is representable as a Number value | |
// Approximately: 2.2204460492503130808472633361816 x 10−16. | |
const epsilon = Number.EPSILON; // The smallest interval between two representable numbers | |
const areAlmostEqual = Math.abs(num1 - num2) < epsilon; | |
console.log("Comparison using epsilon (Math.abs(num1 - num2) < epsilon):", areAlmostEqual); // true - A better way to compare floating point numbers | |
// Explanation: Due to floating-point precision errors, direct comparison of floating-point numbers can lead to unexpected results. | |
// The EPSILON property is the difference between 1 and the smallest value greater than 1 that is representable as a Number. | |
// 3. Rounding Numbers | |
console.log("\nRounding Numbers:"); | |
const numberToRound = 1.005; | |
// Using Math.round | |
const roundedNumber = Math.round(numberToRound * 100) / 100; | |
console.log("Math.round(numberToRound * 100) / 100:", roundedNumber); // 1.01 - Expected rounding to two decimal places | |
// Using toFixed (returns a string) | |
const fixedNumber = numberToRound.toFixed(2); | |
console.log("numberToRound.toFixed(2):", fixedNumber); // "1.01" - String representation | |
// Edge Case: Rounding with Floating Point Errors | |
console.log("Rounding with Floating Point Errors:"); | |
const problematicNumber = 1.335; | |
const roundedProblematicNumber = Math.round(problematicNumber * 100) / 100; | |
console.log("Math.round(problematicNumber * 100) / 100:", roundedProblematicNumber); // 1.33 - Due to floating point precision error | |
// Dangers of Incorrect Rounding | |
console.log("Dangers of Incorrect Rounding:"); | |
const improperRounding = Math.round((0.1 + 0.2) * 10) / 10; | |
console.log("Math.round((0.1 + 0.2) * 10) / 10:", improperRounding); // 0.3 - This works, but be cautious with more complex calculations | |
// | |
// What about BigInt? | |
// | |
// A BigInt value, also sometimes just called a BigInt, is a bigint primitive, created by appending n to the end of an integer literal, | |
// or by calling the BigInt() function (without the new operator) and giving it an integer value or string value. | |
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt | |
const previouslyMaxSafeInteger = 9007199254740991n; | |
const alsoHuge = BigInt(9007199254740991); // 9007199254740991n | |
const hugeString = BigInt("9007199254740991"); // 9007199254740991n | |
const hugeHex = BigInt("0x1fffffffffffff"); // 9007199254740991n | |
const hugeOctal = BigInt("0o377777777777777777"); // 9007199254740991n | |
const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111",); // 9007199254740991n |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment