В интернет-магазине цена товара — 29.99 ₽. Пользователь берёт три штуки. Вы считаете: 29.99 * 3 = 89.97. Но JavaScript выдаёт 89.97000000000001. Отображаете в чеке — и клиент видит некрасивое число. Это не баг — это особенность хранения чисел в памяти компьютера (IEEE 754). Знание числовых методов позволяет работать с этим правильно.
JavaScript использует 64-битные числа с плавающей точкой:
0.1 + 0.2 // 0.30000000000000004 — не 0.3!
0.1 + 0.2 === 0.3 // false
// Решения:
// 1. toFixed — для отображения (возвращает строку!)
(0.1 + 0.2).toFixed(2) // '0.30'
// 2. Храни деньги в целых (копейки, центы)
const priceKopecks = 2999 // 29.99 ₽
const totalKopecks = priceKopecks * 3 // 8997 — точно!
const display = (totalKopecks / 100).toFixed(2) // '89.97'
// 3. Math.round с масштабированием
Math.round(0.1 + 0.2, 10) // не работает так
Math.round((0.1 + 0.2) * 10) / 10 // 0.3Math.round(4.5) // 5 — математическое округление
Math.floor(4.9) // 4 — вниз (всегда меньше)
Math.ceil(4.1) // 5 — вверх (всегда больше)
Math.trunc(4.9) // 4 — отбросить дробную часть (не округлять)
Math.trunc(-4.9) // -4 — в отличие от floor(-4.9) = -5
Math.abs(-7) // 7 — модуль
Math.max(1, 5, 3) // 5
Math.min(1, 5, 3) // 1
Math.sqrt(9) // 3
Math.pow(2, 10) // 1024
Math.log(Math.E) // 1
Math.PI // 3.141592653589793
// Случайное число
Math.random() // [0, 1) — от 0 включительно до 1 не включительноconst n = 1234567.891
// Форматирование в строку
n.toFixed(2) // '1234567.89' — фиксированная точность (строка!)
n.toPrecision(6) // '1234570' — общее кол-во значимых цифр
// Конвертация системы счисления
(255).toString(16) // 'ff' — шестнадцатеричное
(255).toString(2) // '11111111' — двоичное
// Форматирование для отображения (учитывает локаль)
n.toLocaleString('ru-RU') // '1 234 567,891' — рубли
n.toLocaleString('en-US') // '1,234,567.891' — долларыInfinity // бесконечность (1/0)
-Infinity // отрицательная бесконечность
NaN // Not a Number — результат некорректной операции
// Проверки
isNaN(NaN) // true — но: isNaN('hello') тоже true!
Number.isNaN(NaN) // true — строгая проверка
Number.isNaN('hello') // false — это строка, не NaN
isFinite(Infinity) // false
Number.isFinite(1000) // true
Number.isInteger(4.0) // true
Number.isInteger(4.5) // false
Number.MAX_SAFE_INTEGER // 9007199254740991 (2^53 - 1)
Number.MIN_SAFE_INTEGER // -9007199254740991Ошибка 1: toFixed возвращает строку
// Сломано:
const price = (1499.9).toFixed(2)
console.log(price + 0.1) // '1499.900.1' — конкатенация!
// Исправлено — конвертируй в число:
const price = +(1499.9).toFixed(2) // унарный +
// или
const price = parseFloat((1499.9).toFixed(2))Ошибка 2: Math.random() для безопасности
// Math.random() НЕ криптографически безопасен
// Для генерации токенов, паролей используй:
crypto.getRandomValues(new Uint32Array(1))[0] // в браузереОшибка 3: NaN в вычислениях молча всё ломает
const price = Number('неверный ввод') // NaN
const total = price * 3 // NaN — тихо распространяется!
console.log(total > 0) // false — но не ошибка
// Защита:
if (!Number.isFinite(price)) throw new Error('Некорректная цена')toFixed(2)Math.round(rating * 10) / 10 — округление до 1 знакаMath.min(100, Math.floor(done / total * 100)) — процент прогрессаMath.ceil(total / perPage) — количество страницlat.toFixed(6) — 6 знаков для GPS (точность ~11 см)Финансовые вычисления и форматирование для магазина
// Точные финансовые вычисления (цены в копейках)
const cartItems = [
{ name: 'Ноутбук', priceKopecks: 7500000, qty: 1 },
{ name: 'Мышь', priceKopecks: 150000, qty: 2 },
{ name: 'Коврик', priceKopecks: 49900, qty: 3 },
]
const subtotalKopecks = cartItems.reduce((sum, item) => {
return sum + item.priceKopecks * item.qty
}, 0)
// 7500000 + 300000 + 149700 = 7949700 — точно, нет проблем с float
// Скидка 15%
const discountKopecks = Math.round(subtotalKopecks * 0.15)
const totalKopecks = subtotalKopecks - discountKopecks
// Форматирование для отображения
function formatPrice(kopecks, locale = 'ru-RU') {
return (kopecks / 100).toLocaleString(locale, {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 2,
})
}
console.log(formatPrice(subtotalKopecks)) // '79 497,00 ₽'
console.log(formatPrice(discountKopecks)) // '11 924,55 ₽'
console.log(formatPrice(totalKopecks)) // '67 572,45 ₽'
// Случайная цена для акции (от 100 до 500 рублей)
function randomPrice(minRub, maxRub) {
const minK = minRub * 100
const maxK = maxRub * 100
return Math.floor(Math.random() * (maxK - minK + 1)) + minK
}
console.log(formatPrice(randomPrice(100, 500))) // случайная цена
// Прогресс накопления бонусов
const bonusGoal = 100000 // копеек
const bonusCurrent = 67572
const progressPct = Math.min(100, Math.floor(bonusCurrent / bonusGoal * 100))
console.log(`Прогресс: ${progressPct}%`) // 'Прогресс: 67%'
// Количество страниц пагинации
const totalProducts = 247
const perPage = 20
const pages = Math.ceil(totalProducts / perPage)
console.log(`Страниц: ${pages}`) // 'Страниц: 13'В интернет-магазине цена товара — 29.99 ₽. Пользователь берёт три штуки. Вы считаете: 29.99 * 3 = 89.97. Но JavaScript выдаёт 89.97000000000001. Отображаете в чеке — и клиент видит некрасивое число. Это не баг — это особенность хранения чисел в памяти компьютера (IEEE 754). Знание числовых методов позволяет работать с этим правильно.
JavaScript использует 64-битные числа с плавающей точкой:
0.1 + 0.2 // 0.30000000000000004 — не 0.3!
0.1 + 0.2 === 0.3 // false
// Решения:
// 1. toFixed — для отображения (возвращает строку!)
(0.1 + 0.2).toFixed(2) // '0.30'
// 2. Храни деньги в целых (копейки, центы)
const priceKopecks = 2999 // 29.99 ₽
const totalKopecks = priceKopecks * 3 // 8997 — точно!
const display = (totalKopecks / 100).toFixed(2) // '89.97'
// 3. Math.round с масштабированием
Math.round(0.1 + 0.2, 10) // не работает так
Math.round((0.1 + 0.2) * 10) / 10 // 0.3Math.round(4.5) // 5 — математическое округление
Math.floor(4.9) // 4 — вниз (всегда меньше)
Math.ceil(4.1) // 5 — вверх (всегда больше)
Math.trunc(4.9) // 4 — отбросить дробную часть (не округлять)
Math.trunc(-4.9) // -4 — в отличие от floor(-4.9) = -5
Math.abs(-7) // 7 — модуль
Math.max(1, 5, 3) // 5
Math.min(1, 5, 3) // 1
Math.sqrt(9) // 3
Math.pow(2, 10) // 1024
Math.log(Math.E) // 1
Math.PI // 3.141592653589793
// Случайное число
Math.random() // [0, 1) — от 0 включительно до 1 не включительноconst n = 1234567.891
// Форматирование в строку
n.toFixed(2) // '1234567.89' — фиксированная точность (строка!)
n.toPrecision(6) // '1234570' — общее кол-во значимых цифр
// Конвертация системы счисления
(255).toString(16) // 'ff' — шестнадцатеричное
(255).toString(2) // '11111111' — двоичное
// Форматирование для отображения (учитывает локаль)
n.toLocaleString('ru-RU') // '1 234 567,891' — рубли
n.toLocaleString('en-US') // '1,234,567.891' — долларыInfinity // бесконечность (1/0)
-Infinity // отрицательная бесконечность
NaN // Not a Number — результат некорректной операции
// Проверки
isNaN(NaN) // true — но: isNaN('hello') тоже true!
Number.isNaN(NaN) // true — строгая проверка
Number.isNaN('hello') // false — это строка, не NaN
isFinite(Infinity) // false
Number.isFinite(1000) // true
Number.isInteger(4.0) // true
Number.isInteger(4.5) // false
Number.MAX_SAFE_INTEGER // 9007199254740991 (2^53 - 1)
Number.MIN_SAFE_INTEGER // -9007199254740991Ошибка 1: toFixed возвращает строку
// Сломано:
const price = (1499.9).toFixed(2)
console.log(price + 0.1) // '1499.900.1' — конкатенация!
// Исправлено — конвертируй в число:
const price = +(1499.9).toFixed(2) // унарный +
// или
const price = parseFloat((1499.9).toFixed(2))Ошибка 2: Math.random() для безопасности
// Math.random() НЕ криптографически безопасен
// Для генерации токенов, паролей используй:
crypto.getRandomValues(new Uint32Array(1))[0] // в браузереОшибка 3: NaN в вычислениях молча всё ломает
const price = Number('неверный ввод') // NaN
const total = price * 3 // NaN — тихо распространяется!
console.log(total > 0) // false — но не ошибка
// Защита:
if (!Number.isFinite(price)) throw new Error('Некорректная цена')toFixed(2)Math.round(rating * 10) / 10 — округление до 1 знакаMath.min(100, Math.floor(done / total * 100)) — процент прогрессаMath.ceil(total / perPage) — количество страницlat.toFixed(6) — 6 знаков для GPS (точность ~11 см)Финансовые вычисления и форматирование для магазина
// Точные финансовые вычисления (цены в копейках)
const cartItems = [
{ name: 'Ноутбук', priceKopecks: 7500000, qty: 1 },
{ name: 'Мышь', priceKopecks: 150000, qty: 2 },
{ name: 'Коврик', priceKopecks: 49900, qty: 3 },
]
const subtotalKopecks = cartItems.reduce((sum, item) => {
return sum + item.priceKopecks * item.qty
}, 0)
// 7500000 + 300000 + 149700 = 7949700 — точно, нет проблем с float
// Скидка 15%
const discountKopecks = Math.round(subtotalKopecks * 0.15)
const totalKopecks = subtotalKopecks - discountKopecks
// Форматирование для отображения
function formatPrice(kopecks, locale = 'ru-RU') {
return (kopecks / 100).toLocaleString(locale, {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 2,
})
}
console.log(formatPrice(subtotalKopecks)) // '79 497,00 ₽'
console.log(formatPrice(discountKopecks)) // '11 924,55 ₽'
console.log(formatPrice(totalKopecks)) // '67 572,45 ₽'
// Случайная цена для акции (от 100 до 500 рублей)
function randomPrice(minRub, maxRub) {
const minK = minRub * 100
const maxK = maxRub * 100
return Math.floor(Math.random() * (maxK - minK + 1)) + minK
}
console.log(formatPrice(randomPrice(100, 500))) // случайная цена
// Прогресс накопления бонусов
const bonusGoal = 100000 // копеек
const bonusCurrent = 67572
const progressPct = Math.min(100, Math.floor(bonusCurrent / bonusGoal * 100))
console.log(`Прогресс: ${progressPct}%`) // 'Прогресс: 67%'
// Количество страниц пагинации
const totalProducts = 247
const perPage = 20
const pages = Math.ceil(totalProducts / perPage)
console.log(`Страниц: ${pages}`) // 'Страниц: 13'Напиши функцию calcShipping(weightGrams, distanceKm), которая считает стоимость доставки. Правила: базовая ставка 50 ₽, за каждый кг (или его часть) сверх первого — плюс 30 ₽, за каждые 100 км (или их часть) — плюс 20 ₽. Вернуть итоговую стоимость, округлённую до целого рубля. Также напиши formatShipping(price), которая форматирует цену как строку '150 ₽'.
extraKg = Math.max(0, Math.ceil(weightKg) - 1). weightFee = extraKg * 30. distFee = Math.ceil(distanceKm / 100) * 20. Итог = 50 + weightFee + distFee.