Чтобы понять, почему «0,1 + 0,2» не равно «0,3», мы создадим новый формат с плавающей запятой: «Формат почти без точности».
Введение
Чтобы понять, почему «0,1 + 0,2» не равно «0,3», мы создадим новый формат с плавающей запятой. Один намного проще, чем двойная точность; тем не менее, достаточно, чтобы понять проблему, лежащую в основе этого заявления. Этот новый формат, который мы создаем:
Формат с плавающей запятой почти без точности.
С новым форматом мы выполним сложение «0,1 + 0,2» и сравним его с представлением 0,3.
Напоминание: формат с плавающей запятой
Представление с плавающей запятой основано на нормализованной экспоненциальной нотации.
4.9 x 10¹¹
Представление с плавающей запятой сначала преобразует число в экспоненциальное представление, а затем сохраняет части этого обозначения: показатель степени, знак (положительный или отрицательный) и число.
Все числа перед сохранением в памяти преобразуются в двоичные. Поэтому сохраненное число выглядит примерно так:
1.01011 x 2¹⁰¹¹
Давайте посмотрим, как работает формат с плавающей запятой двойной точности.
(-1) * знак * 1.f * 2^(e-1023)
- знак – это бит, указывающий, является ли число положительным или отрицательным.
- "e" — показатель степени со смещением. Пока нет необходимости вдаваться в подробности.
- "f" – это дробная часть (или мантисса): содержание числа. Формат двойной точности определяет пятьдесят два бита для этой части.
Плавающая точка почти без точности
Двойная точность с плавающей запятой хранит пятьдесят два бита для мантиссы, бит знака и одиннадцать бит для экспоненты. Но мы можем упростить формат, если согласимся хранить только числа меньше двух и больше нуля.
Это означает, что мы можем пропустить сохранение битов экспоненты и знака. Что касается количества цифр, которые мы хотим сохранить, давайте согласимся на пять битов для дробной части и один для целой части.
Это все, что нам нужно для нашего представления с плавающей запятой почти без точности.
0,1 + 0,2 в числах с плавающей запятой почти без точности
Не забывайте о нашей цели: мы хотим добавить 0,1 и 0,2.
Во-первых, нам нужно преобразовать 0,1 и 0,2 в формат Почти-нет-точности, а этот формат, как и Двойная точность, также хранит двоичные числа.
Преобразование в числа с плавающей запятой почти без точности
Чтобы преобразовать десятичное число в двоичное, мы преобразуем целую часть и дробную часть отдельно.
Обе целые части равны 0.
Для дробной части в Интернете есть много хороших руководств. Тем не менее, вот суть простого способа сделать это
- Умножьте дробную часть на два.
- Например: «0,1 * 2 = 0,2»
- Следите за целым числом результата: «0»
- Если результат меньше единицы, вернитесь к шагу 1.
- Если результат больше единицы, вычтите единицу и вернитесь к шагу 1.
- Если результат равен нулю, мы закончили. Перейти к шагу 5.
- Дробная часть в двоичном формате - это целые числа результата шага 1.
- Например: если бы целые числа были «0 1 0», дробная часть в двоичном формате была бы: «.010»
Давайте сделаем простой пример преобразования «0,25» в двоичный код.
Плавающая точка почти без точности: 0,1
Если мы посчитаем его для 0,1, то получим следующее:
Результатом преобразования 0,1 в двоичное число является число с повторяющимися цифрами (0,00011001100…). Однако в нашем представлении мы можем хранить только пять цифр.
Плавающая точка почти без точности: 0,2
Поверьте мне, когда я говорю вам, что 0,2 в двоичном коде равно 0,00110011… Он также имеет повторяющийся набор цифр. Или не верьте мне и посчитайте ;)
Добавление числа с плавающей запятой почти без точности
Теперь у нас есть наши числа, сохраненные в памяти. Итак, давайте добавим эти два числа, но помните, что наш компьютер работает с сохраненными двоичными данными, а не с реальными числами.
Это означает, что компьютер выполняет двоичное сложение 0,00011 и 0,00110.
После сложения обоих чисел результат равен 0,01001. Следовательно, это то, что сохраняется в памяти в результате сложения 0,1 и 0,2.
0,1 + 0,2 по сравнению с 0,3 в вычислениях с плавающей запятой почти без точности
Это правильно? Это то, как 0,3 представлено в формате с плавающей запятой почти без точности?
0,3 в двоичном формате — это 0,0100110011… Кроме того, с повторяющимся шаблоном. Итак, как мы храним это в нашей памяти?
В этом случае первая не сохраненная цифра, шестая, равна 1. Это означает, что нам нужно округлить последнюю цифру, которая также равна единице, и распространить округление на остальные цифры, пока не найдем ноль.
Представление 0,3 в нашей памяти равно 0,01010, а сложение 0,01001.
Это означает, что с плавающей запятой почти без точности у нас также есть "0,1 + 0,2 != 0,3".
В чем проблема?
Проблема заключается в попытке представить бесконечную информацию в ограниченном пространстве. Если бы у нас была бесконечная память, мы бы не потеряли никакой точности.
К сожалению, наши компьютеры конечны. Ученые-компьютерщики справлялись с ограничениями компьютеров как могли. Представление с плавающей запятой эффективно, но не идеально.
Назад к реальной жизни
Теперь, когда мы рассмотрели простые примеры, вы, возможно, захотите проверить настоящую вещь. Я рекомендую вам этот отличный пошаговый калькулятор для сложения с плавающей запятой как с одинарной, так и с двойной точностью.
На этом веб-сайте/калькуляторе вы можете увидеть, как ограничения, которые мы узнали в нашем простом примере, также применяются в сложном формате с плавающей запятой двойной точности.
Если вам понравилась эта статья, рассмотрите возможность подписки на мою рассылку GIMTEC.