2017-01-29 18 views
1

Широко известно, что пользователи с плавающей запятой должны следить за ошибками округления. Например, 1.0/3*3 == 1 оценивает значение false в почти каждом современном языке программирования. Более неожиданно для неспециалистов, то есть 1.0/10*10 == 1.Плавающая точка и предотвращение ошибок округления

Однако существуют системы с плавающей запятой, которые по крайней мере кажутся более эффективными с этими проблемами. В частности, я пробовал оба вышеупомянутых теста в эмуляторах Apple II и Commodore Vic-20, и каждое выражение оценивалось как true в каждом случае. Это нелогично: очень примитивные системы работают лучше, чем более продвинутые.

Как старые системы с плавающей точкой получили желаемый ответ в вышеприведенных тестах? Предполагая, что современные плавающие точки IEEE имеют веские основания для этого, что он получает в обмен? Или иначе, в чем проблема, из-за которой старые системы с плавающей запятой были оставлены, хотя они были способны представлять 1/10 и 1/3 без самых неприятных ошибок округления?

Редактировать: Simon Byrne правильно указывает на точные тесты, которые я перечисляю выше, действительно передавайте плавающие точки IEEE. Я не знаю, какую ошибку я совершил с ними, не могу воспроизвести. Но вот один, который терпит неудачу, попробовал в Python только сейчас:

>>> 0.1+0.1+0.1 == 0.3 
False 

Это точный тест успешно на Apple II, так же, как же старая система получить этот результат, а то, что было компромиссом?

+0

Невозможно ответить на историческую часть, но вы можете сохранить это число как неплавающее. Clojure имеет «дробный» класс, который хранит иррациональные числа как фракцию вместо этого, поэтому они не округлены, пока это абсолютно необходимо. – Carcigenicate

+0

@Carcigenicate Правильно, но хранение чисел как фракций действительно нужно делать произвольной точностью, если это должно быть полезно предсказуемо, что приводит к требованию ЦП и памяти, которые исключают его в большинстве случаев, когда мы используем с плавающей запятой. – rwallace

+2

Какой язык/платформа вы используете? '1.0/3 * 3 == 1' и' 1.0/10 * 10 == 1' должны быть истинными на любом языке/платформе с использованием IEEE754 binary64, который в наши дни довольно вездесущ. –

ответ

1

Я предполагаю, что они, вероятно, просто повезло в конкретном примере, который вы выбрали.Например, утверждение верно в IEEE754 binary32 арифметике:

>>> import numpy as np 
>>> np.float32(0.1) + np.float32(0.1) + np.float32(0.1) == np.float32(0.3) 
True 

На основе this posting, то Apple II не обеспечивают аппаратное обеспечение с плавающей точкой, поэтому точные детали зависят от любой при условии, что программное обеспечение (и это звучит, как будто различное программное обеспечение предоставляло различные реализации). Если бы они использовали одно и то же 24-битное значение (или другое, дающее аналогичные результаты), тогда вы увидите тот же ответ.

UPDATE: this document, кажется, указывает, что Applesoft Basic было использовать 24-разрядную мантиссу (не 25 – 24 плюс неявное 1 – как ранее ссылка, казалось бы предложить), что могло бы объяснить, почему вы видели один и тот же результат, как binary32 арифметика.

2

Математически невозможно найти базу для числовой системы, которая может точно представлять все дроби, поскольку существует бесконечно много простых чисел. Если вы хотите точно хранить фракции, вам нужно отдельно хранить числитель и знаменатель, что делает вычисления более сложными.

Если вы храните фракции в виде одного значения, некоторые операции вводят небольшие ошибки. Если они будут выполняться повторно, ошибки будут накапливаться и могут стать заметными.

Есть два пути решения этой проблемы:

  • Если вы можете найти общий знаменатель, масштаб всех значений по этим и использовать целые числа. Например, используйте целые центы вместо долларов с плавающей запятой.

  • Круглые номера, при необходимости. Много раз достаточно печатать числа с плавающей запятой на одну или две цифры меньше их максимальной точности.

Тестирование чисел с плавающей запятой для равенства не может быть выполнено как с целыми числами. Это немного похоже на подсчет двух групп людей и тестирование, если группы имеют одинаковый размер, и тестирование, если две бутылки имеют одинаковое количество молока в них.
Для теста «молоко» вы должны сказать, сколько сумм может отличаться по-прежнему считаться «равным».

У Apple II не было оборудования с плавающей запятой, именно его BASIC разрешил вычисления с плавающей запятой. Я предполагаю, что они включали такую ​​ошибку, связанную с проверкой равенства, или использовали базовые 10 чисел (BCD, см. Комментарий Harold).

+0

И, как мой ответ на harold, BCD, похоже, не объясняет здесь. Ограничение ошибок для тестов равенства может быть возможным объяснением или некоторым альтернативным правилом округления. Я пытаюсь понять, как рассказать обо всем. – rwallace

+0

My Apple II старше, чем арифметика IEEE 754. Даже если он использует двоичную или шестнадцатеричную плавающую точку, правила округления, вероятно, будут разными, поэтому случаи ошибок будут разными. –

+0

@PatriciaShanahan Да, именно так. Теперь, в одном случае, я знаю, что это по-другому, старая система работает лучше. Но IEEE много думал о новой системе. Какие ошибки существуют, когда IEEE работает лучше, чтобы оправдать изменение? – rwallace