Если вы читаете эту статью, то, скорее всего, у вас стресс и проблемы с пониманием математики в вашем приложении. Javascript использует «64-битные числа с плавающей запятой» для представления всех числовых значений на языке. Это свойство, если его не понять, в конечном итоге вызовет множество разочарований у разработчиков и приведет к поломке чувствительных приложений. В этой статье мы быстро рассмотрим, что такое числа с плавающей запятой, и дадим несколько советов по их эффективному использованию.
Что такое числа с плавающей запятой?
Такие термины, как «мантисса», «IEEE 754», «64-битный» и «двоичный» присутствуют почти в каждой статье, затрагивающей эту тему. Давайте избавимся от этих терминов, чтобы вам было легче следовать остальной части статьи. Компьютеры могут хранить информацию только в двоичных форматах, то есть в виде «1» и «0». Но как числа вроде 1,75 представлены в Javascript? Первое, что пытается сделать Javascript, - это преобразовать число в его двоичное значение 1.11. Затем он меняет это на двоичную научную запись. 111 * 10 ^ -10. Помните, что это представление с основанием 2. Таким образом, на самом деле формат, который он сохраняет в памяти, - это m * b ^ e, где m, e и b - мантисса, экспонента и основание, соответственно, хранящиеся в двоичном формате. В Javascript числа с плавающей запятой получают 11 бит пространства для хранения экспоненты, 42 бита для хранения мантиссы и один бит для хранения знака. 1 + 11 + 42 = 64 бита… Тада! Теперь вы знаете, что такое 64-битные числа с плавающей запятой. Если вам нужна дополнительная информация по этой теме, зайдите в Google IEEE 754. Это спецификация, которая подробно описывает, как числа с плавающей запятой обрабатываются в Javascript.
Никогда не предполагайте, что вы знаете, какое десятичное значение вы сохраняете
Давайте посмотрим на картинку выше, чтобы начать хорошее обсуждение.
Как видите, хотя значение `x` было жестко запрограммированный, он хранится как нечто другое! Мы можем увидеть это, заставив консоль печатать более точное представление числа. Здесь очень важно понимать, что ошибки вашего представления могут начаться во время присваивания переменной. Причина этой ошибки присваивания связана с простым фактом, что не все числа с основанием 10 могут быть представлены с основанием 2. Это аналогично тому факту, что мы не можем выразить 1/3 с основанием 10. Итак, как Javascript обрабатывает непредставимые числа в базе 2? Проще говоря, Javascript пытается сохранить как можно больше информации о числе до тех пор, пока мантисса или экспонента не достигнет своей максимальной емкости хранения в битах. Поскольку была сохранена только часть исходного числового значения, разработчики неизбежно столкнутся с ошибками округления с плавающей запятой и начнут вытаскивать волосы. Если вы не можете терпеть ошибок в расчетах в ваших конфиденциальных приложениях, прочтите следующий раздел.
Сохраняйте номера конфиденциальных приложений в виде целых чисел
В сфере финансов и здравоохранения ошибки непростительны. Представьте, что банк случайно делает каждого клиента богаче на 1 цент с каждой транзакцией из-за ошибок вычислений с плавающей запятой. При работе с миллионами клиентов будут потеряны большие деньги. Банки решают эту проблему, храня деньги в целых числах, то есть в центах. Это связано с тем, что целые числа не подвержены ошибкам с плавающей запятой, только десятичные числа. Все целочисленные значения могут быть представлены в базе 2. Это верно, поскольку 1 можно представить как 2⁰, что означает, что все целые числа являются просто суммами 2⁰.
Не сравнивайте десятичные числа с плавающей запятой напрямую
Давайте посмотрим на еще один снимок экрана. Приведенный выше пример печально известен. Левая часть равенства претерпевает больше ошибок округления при введении каждого числа, чем правая часть. Таким образом, прямая проверка равенства двух чисел с плавающей запятой приведет к ошибкам. Хотя проверка на равенство не удалась, фактическая численная разница между этими значениями невероятно мала. Для нечувствительных случаев вы должны сравнить ошибки с плавающей запятой с допустимой погрешностью. Это преобразует вашу проверку равенства с `val1 === val2` на` (val1 - val2)‹ acceptError`. Решение о том, какое значение ошибки является приемлемым, остается за разработчиком. С учетом сказанного, мне нравится использовать значение Javascript Number.EPSILON в качестве допустимой погрешности. Здесь представлены отличные примеры https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
Числа с плавающей запятой - это фантастика, потому что они были оптимизированы для молниеносных вычислений за счет точности. В зависимости от вашего варианта использования приведенные выше советы могут оказаться полезными для вашего развития. Самый важный совет, который я могу дать любому, кто читает эту статью, - это понять, что до тех пор, пока значение не является целым числом, точное значение почти неизвестно.