С детства нас учили, что 0,1 + 0,2 равно 0,3. Однако в загадочном мире вычислений все работает по-другому. Недавно я начал писать код на JavaScript и, читая о типах данных, заметил странное поведение 0,1 + 0,2, не равного 0,3. Я обратился за помощью к Stack Overflow и нашел пару сообщений, которые помогли. Посмотрите ниже:
Проведя много исследований и вычислений, я пришел к выводу, что это не ошибка. Это математика: арифметика с плавающей запятой. Давайте копнем глубже, чтобы понять, что происходит за кулисами.
Постановка задачи: как получилось, что 0,1 + 0,2 = 0,30000000000000004?
Что ж, если вы программировали на таких языках, как Java или C, вы должны знать о различных типах данных, используемых для хранения значений. В предстоящем обсуждении мы будем рассматривать два типа данных: целые числа и числа с плавающей запятой.
Целочисленные типы данных хранят целые числа, а типы данных с плавающей запятой хранят дробные числа.
Прежде чем продолжить, давайте разберемся с одной маленькой концепцией: как числа представлены в вычислительных целях? Очень маленькие и очень большие числа обычно хранятся в экспоненциальном представлении. Они представлены как:
Кроме того, число нормализуется, если оно записано в экспоненциальном представлении с одной ненулевой десятичной цифрой перед десятичной точкой. Например, число 0,0005606 в экспоненциальном представлении и нормализованное будет представлено как:
Significant- это количество значащих цифр, которые не включают нулей, а основание представляет собой используемую базовую систему, которая здесь десятичная (10). Экспонента представляет собой количество мест, на которое необходимо переместить точку счисления влево или вправо для правильного представления.
Теперь есть два способа отображения чисел в арифметике с плавающей запятой: одинарная точность и двойная точность. Одинарная точность использует 32 бита, а двойная точность использует 64 бита для арифметики с плавающей запятой.
В отличие от многих других языков программирования, JavaScript не определяет различные типы числовых типов данных и всегда хранит числа как числа с плавающей запятой двойной точности в соответствии с международным стандартом IEEE 754.
В этом формате числа хранятся в 64 битах, где число (дробь) хранится в битах от 0 до 51, показатель степени - в битах с 52 по 62, а знак - в битах 63.
Давайте представим 0,1 в 64-битном формате в соответствии со стандартом IEEE754.
Первым шагом является преобразование (0,1) основания 10 в его двоичный эквивалент (основание 2).
Для этого мы начнем с умножения 0,1 на 2 и отделим цифру перед десятичной дробью, чтобы получить двоичный эквивалент.
Повторив это для 64 бит, мы собираемся расположить их в порядке возрастания, чтобы получить мантиссу, которую мы собираемся округлить до 52 бит в соответствии со стандартом двойной точности.
Представляя его в научной форме и округляя до первых 52 бит, получим:
Часть мантиссы готова. Теперь для показателя степени мы будем использовать следующий расчет:
Здесь 11 представляет количество битов, которые мы собираемся использовать для 64-битного представления показателя степени, а -4 представляет показатель степени из научной записи.
Окончательное представление числа 0,1:
Точно так же 0.2 будет представлено как:
Добавление двух после того, как экспоненты будут одинаковыми для обоих, даст нам:
В представлении с плавающей запятой это становится:
Это представлено как 0,1 + 0,2.
Это и есть причина получения 0,1 + 0,2 = 0,30000000000000004.
Tihon_V
0.30000000000000004