JavaScript / 39 минут
Числа, операторы и точность
Освойте арифметику, преобразования, NaN и расчёты в целых копейках.
- Освойте арифметику, преобразования, NaN и расчёты в целых копейках.
- Применить тему в задаче «Числа, операторы и точность» и объяснить граничные случаи.
Около 9 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.
Что именно хранится в number
В JavaScript основной числовой тип number используется и для целых значений, и для дробей. В привычной записи 12 и 12.5 не требуют разных объявлений. Внутри большинство чисел представлены в формате IEEE 754 с двойной точностью. Это конечное двоичное представление, поэтому целые числа определённого диапазона хранятся точно, а многие десятичные дроби — приближённо. Ограничение относится к представлению, а не к тому, насколько аккуратно написан оператор сложения.
Для целых чисел безопасный диапазон ограничен Number.MAX_SAFE_INTEGER. За его пределами соседние целые могут перестать различаться. Проверка Number.isSafeInteger полезна для счётчиков и идентификаторов, если контракт действительно предполагает число. Идентификатор из внешней системы часто разумнее хранить строкой: его не нужно складывать, зато нельзя терять цифры. BigInt предназначен для целой арифметики произвольной практической разрядности, но не заменяет десятичные денежные вычисления автоматически.
console.log(typeof 12.5); // number
console.log(Number.isSafeInteger(42)); // true
console.log(9007199254740992 + 1 === 9007199254740992); // true
console.log(12n + 3n); // 15nАрифметика и приоритет операций
Операторы *, / и % имеют более высокий приоритет, чем + и -. Скобки делают намерение явным и особенно полезны в расчёте скидки, среднего значения или границ диапазона. Остаток % не следует бездумно считать математическим модулем для отрицательных чисел: знак результата связан с делимым. Деление на ноль не бросает обычное исключение — результат может стать Infinity или NaN, что необходимо обработать в контракте.
У оператора + есть ещё и строковая семантика. Если один из подходящих операндов превращается в строку, вместо сложения получится конкатенация. Поэтому значение из поля ввода нельзя сразу складывать с числом. Сначала выполните осознанное преобразование, затем проверьте результат, и только после этого считайте итог. Такой порядок предотвращает ситуацию, когда количество 2 и цена '10' неожиданно образуют текст.
console.log(2 + 3 * 4); // 14
console.log((2 + 3) * 4); // 20
console.log('10' + 2); // 102
console.log(Number('10') + 2); // 12
console.log(-7 % 3); // -1Явное преобразование и NaN
Number преобразует значение целиком. Number('12px') даёт NaN, а parseInt('12px', 10) извлекает допустимый начальный числовой фрагмент. Эти функции решают разные задачи. Для строгого количества товаров принимать '12px' как 12 обычно неправильно. Проверяйте разрешённую форму строки до преобразования, если формат является частью условия.
NaN означает неуспешный числовой результат, но typeof NaN остаётся 'number'. Более того, NaN не равно самому себе. Используйте Number.isNaN для проверки именно этого значения, а Number.isFinite — когда требуется обычное конечное число без NaN и бесконечностей. Пустая строка при Number превращается в 0: если пустой ввод означает отсутствие, обработайте его раньше, а не принимайте получившийся ноль за сознательный ответ пользователя.
console.log(Number('12px')); // NaN
console.log(parseInt('12px', 10)); // 12
console.log(Number.isNaN(Number('ошибка'))); // true
console.log(Number.isFinite(Infinity)); // falseОкругление и дробная точность
Math.floor округляет вниз, Math.ceil вверх, Math.trunc отбрасывает дробную часть, Math.round округляет до ближайшего целого с определённым правилом для половин. На отрицательных числах различие floor и trunc особенно заметно. Выбирайте операцию по смыслу задачи: число необходимых коробок округляют вверх, а не просто обрезают остаток.
console.log(Math.floor(-1.7)); // -2
console.log(Math.trunc(-1.7)); // -1
console.log(Math.ceil(11 / 5)); // 3 коробки
console.log(0.1 + 0.2); // 0.30000000000000004Для сравнения результатов вычислений иногда применяют допуск, но универсальное добавление Number.EPSILON не решает все численные задачи. Подходящий допуск зависит от масштаба величин и требований предметной области. Для отображения toFixed возвращает строку; форматирование не меняет того, как исходные числа хранятся и складываются.
Деньги и целая минимальная единица
Если задача ограничивает цену двумя десятичными знаками, удобно преобразовать каждую цену в целое число копеек, сложить целые и только затем вернуть сумму в рублях. Так вы выбираете точную единицу расчёта для указанного контракта. В сложной финансовой системе отдельно определяют правила округления, валюты и допустимый диапазон; учебный приём не является заменой полноценной модели денег.
Не округляйте промежуточный итог после каждого произвольного действия без правила: порядок операций может повлиять на результат. В нашей задаче правило задано явно — каждая входная цена переводится в копейки отдельно. Это позволяет проверить результат воспроизводимо и не смешивать арифметику с красивым отображением строки.
Проверка решения и перенос навыка
Проверьте сумму пустого массива, одной цены, нескольких дробных цен и нулей. Отдельно проследите 0.1 и 0.2: ожидаемый денежный результат — 0.3, а не текст '0.30'. Если возвращается строка, контракт нарушен, даже когда визуально она выглядит правильно. Запишите единицу каждого промежуточного значения: вход в рублях, аккумулятор в копейках, результат снова в рублях.
Для самостоятельного продолжения напишите функцию количества упаковок и объясните, почему у неё другое правило округления. Затем сравните обработку '', '12', '12px', null и undefined через Number. Сначала предскажите результат и только потом проверьте его. Эта привычка помогает не угадывать поведение преобразований в реальном HTTP-вводе и формах.