Округление чисел в JavaScript: как получить 2 знака после запятой (и не больше)

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Магия чисел: почему 0.1 + 0.2 не равно 0.3?

Привет, будущий гуру JavaScript! Если ты когда-нибудь пытался сложить 0.1 и 0.2 в консоли браузера, то, скорее всего, испытал культурный шок:

console.log(0.1 + 0.2); // Выведет: 0.30000000000000004

Да-да, JavaScript не сошел с ума. Это стандартная проблема арифметики с плавающей точкой (IEEE 754). Компьютеры просто не могут идеально представить 0.1 в двоичной системе — это как пытаться записать 1/3 в десятичной. Получается бесконечная дробь.

И тут возникает классический вопрос с Stack Overflow, который набрал тысячи просмотров: «How to round to at most 2 decimal places, if necessary». Как вывести число красиво, без хвоста из «0000000004»? Как округлить до двух знаков, но только если это нужно?

В этой статье мы разберем пошаговый туториал, который решит проблему раз и навсегда. Ты узнаешь, почему toFixed() иногда врет, как работает Math.round() и какой метод выбрать для интернет-магазина, калькулятора или просто для красивого вывода цены.

Проблема: «Хочу 2 знака, но не больше!»

Представь, что ты пишешь корзину товаров. Цена товара — 10.00. Ты хочешь показать 10, а не 10.00. Но если цена 10.456, ты хочешь показать 10.46. Логика простая: округляем до двух знаков, но лишние нули отбрасываем.

Стандартный метод toFixed(2) делает так:

const price = 10;
console.log(price.toFixed(2)); // "10.00" — а нам нужно "10"

const price2 = 10.456;
console.log(price2.toFixed(2)); // "10.46" — тут ок

Проблема: toFixed() всегда возвращает строку и всегда добавляет нули. Это не подходит.

Второй нюанс — toFixed() может округлять странно из-за всё той же плавающей точки:

console.log((1.005).toFixed(2)); // "1.00" — а должно быть "1.01"!

Бинго! Вот почему вопрос на Stack Overflow так популярен. Давай разбираться с решениями.

Решение №1: Математическое округление (Math.round)

Самый надежный способ — это умножить число на 100, округлить, разделить обратно. Работает это так:

function roundToTwo(num) {
  return Math.round(num * 100) / 100;
}

console.log(roundToTwo(10.456)); // 10.46
console.log(roundToTwo(10));     // 10
console.log(roundToTwo(1.005)); // 1 — упс, опять проблема

Видишь? 1.005 * 100 = 100.49999999999999 (из-за IEEE 754). Math.round округляет это вниз, получается 100, делим на 100 = 1. Нехорошо.

Вывод: Метод хорош для простых случаев, но не для финансовых расчетов. Для учебных примеров — пойдет.

Решение №2: Number.EPSILON — спаситель точности

Чтобы победить ошибку плавающей точки, можно добавить крошечную дельту — Number.EPSILON. Это минимальная разница между двумя числами, которую может различить JavaScript (примерно 2.2e-16).

function roundToTwo(num) {
  return Math.round((num + Number.EPSILON) * 100) / 100;
}

console.log(roundToTwo(1.005)); // 1.01 — работает!
console.log(roundToTwo(10.456)); // 10.46
console.log(roundToTwo(10));     // 10

Почему это работает? Мы подталкиваем число чуть-чуть вверх, чтобы 100.4999... превратилось в 100.5... и округлилось правильно.

Важно: Этот метод не идеален для всех крайних случаев, но для 99% задач — отличное решение. Именно его часто советуют на Stack Overflow.

Решение №3: toFixed() + Number() — хитрый трюк

Еще один популярный способ — использовать toFixed(2), а потом преобразовать строку обратно в число через Number() или унарный плюс +. Лишние нули отвалятся сами:

function roundToTwo(num) {
  return Number(num.toFixed(2));
}

console.log(roundToTwo(10.456)); // 10.46
console.log(roundToTwo(10));     // 10
console.log(roundToTwo(1.005)); // 1 — опять проблема!

Да, toFixed() сам по себе страдает от той же ошибки с 1.005. Но если твои числа не содержат таких «опасных» значений (а в реальных ценах это редкость), этот метод самый лаконичный.

Решение №4: toLocaleString — для красивого вывода

Если тебе нужно не просто округлить, а еще и отформатировать число для пользователя (с разделителями тысяч), используй toLocaleString:

function formatPrice(num) {
  return num.toLocaleString('ru-RU', {
    minimumFractionDigits: 0,
    maximumFractionDigits: 2
  });
}

console.log(formatPrice(1234.5));   // "1 234,5"
console.log(formatPrice(100));      // "100"
console.log(formatPrice(99.999));   // "100"

Этот метод не округляет в строгом смысле, он форматирует. И он идеально подходит для вывода цен в интерфейсе. maximumFractionDigits: 2 гарантирует, что дробных знаков будет не больше двух.

Решение №5: Универсальная функция (лучшая практика)

Давай соберем всё лучшее в одну функцию. Она будет округлять до любого количества знаков и обрабатывать крайние случаи:

/**
 * Округляет число до указанного количества знаков после запятой.
 * Убирает лишние нули.
 * 
 * @param {number} num - исходное число
 * @param {number} decimals - количество знаков (по умолчанию 2)
 * @returns {number} округленное число
 */
function roundTo(num, decimals = 2) {
  const factor = Math.pow(10, decimals);
  return Math.round((num + Number.EPSILON) * factor) / factor;
}

// Тесты
console.log(roundTo(10.456, 2)); // 10.46
console.log(roundTo(10, 2));     // 10
console.log(roundTo(1.005, 2)); // 1.01
console.log(roundTo(5.999, 2));  // 6
console.log(roundTo(0.1 + 0.2, 2)); // 0.                

Блоги

Рекомендаций по книгам