Бэкенд на Java вернул ID транзакции: { id: 9007199254740999 }. JavaScript молча сохранил его как 9007199254740992 — другое число! Это не баг сервера и не баг бэкенда: это ограничение типа Number. Для работы с такими числами существует BigInt.
bigint — восьмойТип Number может точно представлять целые числа только до определённого предела:
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991 (2^53 - 1)
console.log(Number.MAX_SAFE_INTEGER + 1) // 9007199254740992 — ещё точно
console.log(Number.MAX_SAFE_INTEGER + 2) // 9007199254740992 — ОШИБКА!
console.log(Number.MAX_SAFE_INTEGER + 3) // 9007199254740994Это критично для работы с ID из базы данных: если сервер возвращает { id: 9007199254740993 }, JavaScript сохранит его как 9007199254740992.
Создать BigInt можно двумя способами:
// 1. Суффикс n
const bigId = 9007199254740993n
const balance = 100_000_000n // разделители для читаемости
// 2. Функция BigInt()
const fromNumber = BigInt(42) // 42n
const fromString = BigInt('12345678901234567890') // очень большое число
console.log(typeof bigId) // 'bigint'
console.log(bigId) // 9007199254740993nconst a = 1000000000000000000n
const b = 2000000000000000000n
console.log(a + b) // 3000000000000000000n
console.log(b - a) // 1000000000000000000n
console.log(a * 3n) // 3000000000000000000n
console.log(b / a) // 2n (деление всегда целочисленное!)
console.log(b % 3n) // 2n
console.log(2n ** 64n) // 18446744073709551616n (2^64)Важно: деление BigInt всегда целочисленное — дробная часть отбрасывается:
console.log(7n / 2n) // 3n, а не 3.5n!
console.log(10n / 3n) // 3nconst big = 100n
const num = 5
// big + num // TypeError: Cannot mix BigInt and other types!
// Нужно явное преобразование:
console.log(big + BigInt(num)) // 105n
console.log(Number(big) + num) // 105 (но рискуем потерять точность!)// Нестрогое сравнение (==) — работает:
console.log(100n == 100) // true
console.log(100n == '100') // true
// Строгое сравнение (===) — всегда false:
console.log(100n === 100) // false (разные типы!)
console.log(typeof 100n) // 'bigint'
console.log(typeof 100) // 'number'
// Операторы сравнения работают корректно:
console.log(100n > 50) // true
console.log(50n < 100) // true// Финансовые расчёты в копейках (избегаем ошибок float)
const price = 1999n // 19.99 руб в копейках
const quantity = 1000n
const discount = 15n // 15%
const subtotal = price * quantity // 1999000n
const discountAmount = subtotal * discount / 100n // 299850n
const total = subtotal - discountAmount // 1699150n
// Форматирование для вывода:
function formatKopecks(kopecks) {
const rub = kopecks / 100n
const kop = kopecks % 100n
return `${rub}.${String(kop).padStart(2, '0')} ₽`
}
console.log(formatKopecks(total)) // '16991.50 ₽'1. Смешивание BigInt и Number в арифметике:
// Плохо: TypeError
const id = 9007199254740993n
const offset = 10
console.log(id + offset) // TypeError: Cannot mix BigInt and other types
// Хорошо: явное преобразование
console.log(id + BigInt(offset)) // 9007199254741003n2. Использование Math-функций с BigInt:
// Плохо: Math не работает с BigInt
Math.max(1n, 2n) // TypeError
Math.abs(-5n) // TypeError
// Хорошо: ручное сравнение
function maxBigInt(a, b) { return a > b ? a : b }
console.log(maxBigInt(1n, 2n)) // 2n3. JSON.stringify не сериализует BigInt:
// Плохо:
JSON.stringify({ id: 9007199254740993n })
// TypeError: Do not know how to serialize a BigInt
// Хорошо: конвертируй в строку для сериализации
JSON.stringify({ id: String(9007199254740993n) })
// '{"id":"9007199254740993"}'bigint может быть больше MAX_SAFE_INTEGER; ID Твиттера уже требует BigIntПотеря точности с большими числами vs BigInt, вычисление факториала
// 1. Демонстрация потери точности
console.log('--- Проблема Number ---')
const maxSafe = Number.MAX_SAFE_INTEGER
console.log(maxSafe) // 9007199254740991
console.log(maxSafe + 1) // 9007199254740992
console.log(maxSafe + 2) // 9007199254740992 (должно быть 9007199254740993!)
console.log(maxSafe + 3) // 9007199254740994
console.log(maxSafe + 4) // 9007199254740996 (должно быть 9007199254740995!)
// То же самое с BigInt — без потери точности
console.log('\n--- BigInt точен ---')
const bigMax = BigInt(maxSafe)
console.log(bigMax) // 9007199254740991n
console.log(bigMax + 1n) // 9007199254740992n
console.log(bigMax + 2n) // 9007199254740993n (точно!)
console.log(bigMax + 3n) // 9007199254740994n
console.log(bigMax + 4n) // 9007199254740995n (точно!)
// 2. Факториал — число быстро выходит за MAX_SAFE_INTEGER
console.log('\n--- Факториал с Number ---')
function factorialNumber(n) {
if (n <= 1) return 1
return n * factorialNumber(n - 1)
}
console.log(factorialNumber(20)) // 2432902008176640000 — выглядит правильно
console.log(factorialNumber(21)) // 51090942171709440000 — уже неточно!
// 3. Факториал с BigInt — точный результат
console.log('\n--- Факториал с BigInt ---')
function factorialBigInt(n) {
if (n <= 1n) return 1n
return n * factorialBigInt(n - 1n)
}
console.log(factorialBigInt(20n))
// 2432902008176640000n — точно!
console.log(factorialBigInt(50n))
// 30414093201713378043612608166979581188299763898377856000000000000n
// 4. Финансы в копейках
console.log('\n--- Финансы ---')
const price = 1999n // 19.99 руб
const qty = 500n
const discount = 10n // 10%
const subtotal = price * qty
const discounted = subtotal - subtotal * discount / 100n
console.log(`Итого: ${discounted / 100n}.${discounted % 100n} ₽`)
// Итого: 8996.50 ₽ (нет ошибок плавающей точки!)
// 5. Сравнение BigInt и Number
console.log('\n--- Сравнение ---')
console.log(100n == 100) // true (нестрогое)
console.log(100n === 100) // false (строгое — разные типы!)
console.log(100n > 50) // true
console.log(typeof 42n) // 'bigint'Бэкенд на Java вернул ID транзакции: { id: 9007199254740999 }. JavaScript молча сохранил его как 9007199254740992 — другое число! Это не баг сервера и не баг бэкенда: это ограничение типа Number. Для работы с такими числами существует BigInt.
bigint — восьмойТип Number может точно представлять целые числа только до определённого предела:
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991 (2^53 - 1)
console.log(Number.MAX_SAFE_INTEGER + 1) // 9007199254740992 — ещё точно
console.log(Number.MAX_SAFE_INTEGER + 2) // 9007199254740992 — ОШИБКА!
console.log(Number.MAX_SAFE_INTEGER + 3) // 9007199254740994Это критично для работы с ID из базы данных: если сервер возвращает { id: 9007199254740993 }, JavaScript сохранит его как 9007199254740992.
Создать BigInt можно двумя способами:
// 1. Суффикс n
const bigId = 9007199254740993n
const balance = 100_000_000n // разделители для читаемости
// 2. Функция BigInt()
const fromNumber = BigInt(42) // 42n
const fromString = BigInt('12345678901234567890') // очень большое число
console.log(typeof bigId) // 'bigint'
console.log(bigId) // 9007199254740993nconst a = 1000000000000000000n
const b = 2000000000000000000n
console.log(a + b) // 3000000000000000000n
console.log(b - a) // 1000000000000000000n
console.log(a * 3n) // 3000000000000000000n
console.log(b / a) // 2n (деление всегда целочисленное!)
console.log(b % 3n) // 2n
console.log(2n ** 64n) // 18446744073709551616n (2^64)Важно: деление BigInt всегда целочисленное — дробная часть отбрасывается:
console.log(7n / 2n) // 3n, а не 3.5n!
console.log(10n / 3n) // 3nconst big = 100n
const num = 5
// big + num // TypeError: Cannot mix BigInt and other types!
// Нужно явное преобразование:
console.log(big + BigInt(num)) // 105n
console.log(Number(big) + num) // 105 (но рискуем потерять точность!)// Нестрогое сравнение (==) — работает:
console.log(100n == 100) // true
console.log(100n == '100') // true
// Строгое сравнение (===) — всегда false:
console.log(100n === 100) // false (разные типы!)
console.log(typeof 100n) // 'bigint'
console.log(typeof 100) // 'number'
// Операторы сравнения работают корректно:
console.log(100n > 50) // true
console.log(50n < 100) // true// Финансовые расчёты в копейках (избегаем ошибок float)
const price = 1999n // 19.99 руб в копейках
const quantity = 1000n
const discount = 15n // 15%
const subtotal = price * quantity // 1999000n
const discountAmount = subtotal * discount / 100n // 299850n
const total = subtotal - discountAmount // 1699150n
// Форматирование для вывода:
function formatKopecks(kopecks) {
const rub = kopecks / 100n
const kop = kopecks % 100n
return `${rub}.${String(kop).padStart(2, '0')} ₽`
}
console.log(formatKopecks(total)) // '16991.50 ₽'1. Смешивание BigInt и Number в арифметике:
// Плохо: TypeError
const id = 9007199254740993n
const offset = 10
console.log(id + offset) // TypeError: Cannot mix BigInt and other types
// Хорошо: явное преобразование
console.log(id + BigInt(offset)) // 9007199254741003n2. Использование Math-функций с BigInt:
// Плохо: Math не работает с BigInt
Math.max(1n, 2n) // TypeError
Math.abs(-5n) // TypeError
// Хорошо: ручное сравнение
function maxBigInt(a, b) { return a > b ? a : b }
console.log(maxBigInt(1n, 2n)) // 2n3. JSON.stringify не сериализует BigInt:
// Плохо:
JSON.stringify({ id: 9007199254740993n })
// TypeError: Do not know how to serialize a BigInt
// Хорошо: конвертируй в строку для сериализации
JSON.stringify({ id: String(9007199254740993n) })
// '{"id":"9007199254740993"}'bigint может быть больше MAX_SAFE_INTEGER; ID Твиттера уже требует BigIntПотеря точности с большими числами vs BigInt, вычисление факториала
// 1. Демонстрация потери точности
console.log('--- Проблема Number ---')
const maxSafe = Number.MAX_SAFE_INTEGER
console.log(maxSafe) // 9007199254740991
console.log(maxSafe + 1) // 9007199254740992
console.log(maxSafe + 2) // 9007199254740992 (должно быть 9007199254740993!)
console.log(maxSafe + 3) // 9007199254740994
console.log(maxSafe + 4) // 9007199254740996 (должно быть 9007199254740995!)
// То же самое с BigInt — без потери точности
console.log('\n--- BigInt точен ---')
const bigMax = BigInt(maxSafe)
console.log(bigMax) // 9007199254740991n
console.log(bigMax + 1n) // 9007199254740992n
console.log(bigMax + 2n) // 9007199254740993n (точно!)
console.log(bigMax + 3n) // 9007199254740994n
console.log(bigMax + 4n) // 9007199254740995n (точно!)
// 2. Факториал — число быстро выходит за MAX_SAFE_INTEGER
console.log('\n--- Факториал с Number ---')
function factorialNumber(n) {
if (n <= 1) return 1
return n * factorialNumber(n - 1)
}
console.log(factorialNumber(20)) // 2432902008176640000 — выглядит правильно
console.log(factorialNumber(21)) // 51090942171709440000 — уже неточно!
// 3. Факториал с BigInt — точный результат
console.log('\n--- Факториал с BigInt ---')
function factorialBigInt(n) {
if (n <= 1n) return 1n
return n * factorialBigInt(n - 1n)
}
console.log(factorialBigInt(20n))
// 2432902008176640000n — точно!
console.log(factorialBigInt(50n))
// 30414093201713378043612608166979581188299763898377856000000000000n
// 4. Финансы в копейках
console.log('\n--- Финансы ---')
const price = 1999n // 19.99 руб
const qty = 500n
const discount = 10n // 10%
const subtotal = price * qty
const discounted = subtotal - subtotal * discount / 100n
console.log(`Итого: ${discounted / 100n}.${discounted % 100n} ₽`)
// Итого: 8996.50 ₽ (нет ошибок плавающей точки!)
// 5. Сравнение BigInt и Number
console.log('\n--- Сравнение ---')
console.log(100n == 100) // true (нестрогое)
console.log(100n === 100) // false (строгое — разные типы!)
console.log(100n > 50) // true
console.log(typeof 42n) // 'bigint'В криптографическом модуле нужна точная математика. Напиши функцию factorial(n), которая принимает BigInt и возвращает точный факториал. Функция должна корректно вычислять 20! = 2432902008176640000n.
Базовый случай: if (n <= 1n) return 1n — сравниваем BigInt с BigInt. Рекурсия: return n * factorial(n - 1n) — все операции с BigInt. Убедись, что аргумент функции тоже BigInt: factorial(20n), а не factorial(20).