Skip to content

Instantly share code, notes, and snippets.

@felipeblassioli
Last active June 12, 2024 16:23
Show Gist options
  • Save felipeblassioli/33b8dd464b3a47acf9fa08e2f2e80b41 to your computer and use it in GitHub Desktop.
Save felipeblassioli/33b8dd464b3a47acf9fa08e2f2e80b41 to your computer and use it in GitHub Desktop.
//
// É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