Чтобы понять, почему «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.

Для дробной части в Интернете есть много хороших руководств. Тем не менее, вот суть простого способа сделать это

  1. Умножьте дробную часть на два.
  2. Например: «0,1 * 2 = 0,2»
  3. Следите за целым числом результата: «0»
  4. Если результат меньше единицы, вернитесь к шагу 1.
  5. Если результат больше единицы, вычтите единицу и вернитесь к шагу 1.
  6. Если результат равен нулю, мы закончили. Перейти к шагу 5.
  7. Дробная часть в двоичном формате - это целые числа результата шага 1.
  8. Например: если бы целые числа были «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.