// ───────────────────────────────────────────────────────────────────────────── // JS NUMBERS & MATH — everything is a double; integer division doesn't exist // ───────────────────────────────────────────────────────────────────────────── // DIVIDE : Math.floor(a / b) for ints >= 0; Math.trunc toward zero; % keeps the // dividend's sign (use ((a % n) + n) % n for a positive modulo). // PARSE : Number(s) strict ('' -> 0!), parseInt(s, 10) lenient prefix, // parseFloat, +s. Number.isNaN not isNaN. Number.isInteger. // FORMAT : toFixed(n) returns a STRING. Math.round rounds .5 toward +Infinity. // LIMITS : Number.MAX_SAFE_INTEGER ~9e15; BigInt (123n) beyond; bitwise ops are // 32-bit signed. // SAY : "IEEE-754 double", "floating-point error", "safe integer", "BigInt", // "integer cents for money", "overflow doesn't throw — it goes to Infinity" // ───────────────────────────────────────────────────────────────────────────── 'use strict'; const assert = require('node:assert/strict'); // ── 1. Division & modulo assert.equal(7 / 2, 3.5); assert.equal(Math.floor(7 / 2), 3); assert.equal(Math.trunc(-7 / 2), -3); // floor(-3.5) = -4 assert.equal((7 / 2) | 0, 3); // bitwise trunc — 32-bit only assert.equal(-7 % 3, -1); const mod = (a, n) => ((a % n) + n) % n; assert.equal(mod(-7, 3), 2); assert.equal(5 % 1, 0); assert.equal(5.5 % 1, 0.5); // fractional part assert.equal(1 / 0, Infinity); // no exception assert.equal(Number.isNaN(0 / 0), true); // ── 2. Parsing assert.equal(Number('42'), 42); assert.equal(Number('42abc'), NaN); assert.equal(Number(''), 0); // gotcha assert.equal(Number(' 12 '), 12); assert.equal(Number(null), 0); assert.equal(Number(undefined), NaN); assert.equal(Number(true), 1); assert.equal(Number('0x1f'), 31); assert.equal(parseInt('42abc', 10), 42); // ALWAYS pass radix assert.equal(parseInt('abc', 10), NaN); assert.equal(parseFloat('3.14xyz'), 3.14); assert.equal(+'3' + +'4', 7); // unary plus assert.equal('3' + 4, '34'); // string wins with + assert.equal('3' * 4, 12); // other operators coerce to number assert.equal(Number.isNaN('x'), false); // Number.isNaN: no coercion assert.equal(isNaN('x'), true); // global isNaN coerces — avoid assert.equal(Number.isInteger(5.0), true); assert.equal(Number.isInteger('5'), false); assert.equal(Number.isFinite(1 / 0), false); // ── 3. Formatting assert.equal((2.5).toFixed(2), '2.50'); // STRING assert.equal((1234.5678).toFixed(1), '1234.6'); assert.equal((1.005).toFixed(2), '1.00'); // FP: 1.005 is really 1.00499999... assert.equal(Math.round(2.5), 3); assert.equal(Math.round(-2.5), -2); // toward +Infinity, not away from zero assert.equal(Math.round(1.005 * 100) / 100, 1); // "round to 2 places" — still FP-prone const round2 = x => Math.round((x + Number.EPSILON) * 100) / 100; assert.equal(round2(1.005), 1.01); assert.equal((255).toString(16), 'ff'); assert.equal((255).toString(2), '11111111'); assert.equal((5).toString().padStart(3, '0'), '005'); assert.equal((1234567.891).toLocaleString('en-US'), '1,234,567.891'); assert.equal(Number((0.1 + 0.2).toFixed(10)), 0.3); // ── 4. Floating point assert.equal(0.1 + 0.2 === 0.3, false); assert.equal(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON, true); assert.equal((10 + 20) / 100, 0.3); // money: integer cents, divide at the end assert.equal(Number.MAX_SAFE_INTEGER, 9007199254740991); // 2^53 - 1 assert.equal(9007199254740992 === 9007199254740993, true); // precision lost past MAX_SAFE_INTEGER assert.equal(Number.isSafeInteger(2 ** 53), false); // ── 5. BigInt — when the problem says "up to 10^18" or factorials assert.equal(2n ** 64n, 18446744073709551616n); assert.equal(BigInt(10) ** 18n > BigInt(Number.MAX_SAFE_INTEGER), true); assert.equal(typeof 1n, 'bigint'); assert.equal(7n / 2n, 3n); // integer division built in assert.throws(() => 1n + 1); // can't mix; convert with BigInt(x) / Number(b) assert.equal(String(123n), '123'); assert.equal(1n == 1, true); // loose equality works; strict doesn't assert.equal(1n === 1, false); // ── 6. Math grab-bag assert.equal(Math.abs(-3), 3); assert.equal(Math.sign(-3), -1); assert.equal(Math.max(), -Infinity); // of nothing — guard empty arrays assert.equal(Math.min(), Infinity); assert.equal(2 ** 10, 1024); assert.equal(Math.pow(2, 10), 1024); assert.equal(Math.sqrt(16), 4); assert.equal(Math.cbrt(27), 3); assert.equal(Math.hypot(3, 4), 5); assert.equal(Math.ceil(4.1), 5); assert.equal(Math.log2(8), 3); assert.equal(Math.log10(1000), 3); assert.equal(Math.floor(Math.log10(12345)) + 1, 5); // digit count assert.equal(String(12345).length, 5); // digit count, simpler const gcd = (a, b) => (b ? gcd(b, a % b) : Math.abs(a)); const lcm = (a, b) => (a / gcd(a, b)) * b; assert.equal(gcd(12, 18), 6); assert.equal(lcm(4, 6), 12); const isPrime = n => { if (n < 2) return false; for (let i = 2; i * i <= n; i++) if (n % i === 0) return false; return true; }; assert.equal(isPrime(97), true); const digits = n => [...String(Math.abs(n))].map(Number); assert.deepEqual(digits(-402), [4, 0, 2]); const digitSum = n => digits(n).reduce((a, b) => a + b, 0); assert.equal(digitSum(29), 11); const reverseNum = n => Math.sign(n) * Number([...String(Math.abs(n))].reverse().join('')); assert.equal(reverseNum(-120), -21); const clamp = (x, lo, hi) => Math.min(hi, Math.max(lo, x)); assert.equal(clamp(15, 0, 10), 10); // Random (not for crypto): Math.floor(Math.random() * n) in [0, n). Tokens -> node:crypto randomBytes. // ── 7. Bitwise — 32-bit SIGNED; >>> is unsigned assert.equal(5 & 3, 1); assert.equal(5 | 3, 7); assert.equal(5 ^ 3, 6); assert.equal(~5, -6); // -(n+1) assert.equal(1 << 3, 8); assert.equal(-8 >> 1, -4); // arithmetic shift keeps sign assert.equal(-1 >>> 0, 4294967295); // unsigned view assert.equal(2 ** 31 | 0, -2147483648); // overflow wraps in bitwise ops assert.equal((8 & (8 - 1)) === 0, true); // power of two assert.equal(6 & -6, 2); // lowest set bit assert.equal((13).toString(2).split('1').length - 1, 3); // popcount the lazy way // ── 8. Infinity / sentinels let best = -Infinity; for (const x of [3, 9, 2]) best = Math.max(best, x); assert.equal(best, 9); assert.equal(Infinity > Number.MAX_VALUE, true); assert.equal(JSON.stringify({ a: Infinity, b: NaN }), '{"a":null,"b":null}'); // JSON can't carry them console.log('ok: javascript/numbers-math.js');