Сомнения в простой арифметике - PullRequest
0 голосов
/ 13 октября 2018

Я начинаю изучать Haskell, используя знаменитую книгу Мирана Липоваки , но мое любопытство остановило меня при первых взаимодействиях с интерактивной оболочкой Glasgow Haskell Compiler (ghci).

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

λ> 1/3
0.3333333333333333
λ> 4/3
1.3333333333333333
λ> 3424/3
1141.3333333333333

Они сказали мне, что Haskell использует всего 17цифры (или 18 символов, включая точку?), независимо от того, значимы они или нет.Однако они также встречаются

λ> 14/3
4.666666666666667
λ> 34/3
11.333333333333334
λ> 44/3
14.666666666666666

Почему первая короче на одну цифру?Почему остальные ошибочно округлены?

Возможно, это глупый вопрос, но я чувствую, что, зная ответ на такие базовые вещи, я могу начать с более глубокого понимания того, как работает язык (или переводчик?), Зная, чтонемного больше того, что находится под капотом.

1 Ответ

0 голосов
/ 13 октября 2018

Спецификация Haskell оставляет некоторую слабость в спецификации форматов и поведений с плавающей запятой.В нем говорится, что тип Haskell Double должен охватывать IEEE «двойную точность» по дальности и точности, операции по умолчанию, определенные в Haskell Prelude, не соответствуют определенным стандартам, но некоторые аспекты IEEE с плавающей запятой были учтены в Preludeкласс RealFloat.В этом ответе я продемонстрирую, как результаты в вопросе возникают из базового 64-разрядного двоичного формата IEEE-754 и арифметики.

Состояния вопроса:

Они сказали мне, что Haskell использует всего 17 цифр (или 18 символов, включая точку?), Независимо от того, значимы они или нет.

Это неверно.Как предполагается в этом ответе и, вероятно, имеет место в реализации OP на Haskell, число имеет 53 двоичных разряда, а не 17 десятичных разрядов.Могут отображаться 17 цифр, но это результат преобразования числа для отображения, а не точное представление фактического значения, используемого для вычислений.

Первые три показанных случая не примечательны, но, для иллюстрации,мы покажем внутренние значения:

λ> 1/3
0.3333333333333333 -- 0.333333333333333314829616256247390992939472198486328125
λ> 4/3
1.3333333333333333 -- 1.3333333333333332593184650249895639717578887939453125
λ> 3424/3
1141.3333333333333 -- 1141.333333333333257542108185589313507080078125

Теперь рассмотрим удивительные случаи, начиная с:

λ> 14/3
4.666666666666667

Что удивительно, так это то, что он показан с 16 десятичными цифрами,тогда как предыдущие результаты были показаны с 17. Почему?

Я не вижу правил в спецификации Haskell о том, как числа с плавающей запятой должны форматироваться при отображении или иным образом преобразовываться в десятичное число.Это объясняет одно правило, принятое Java и некоторым другим программным обеспечением: создайте достаточно десятичных цифр, чтобы преобразовать десятичную цифру обратно в формат с плавающей запятой и получить исходное число.То есть, достаточно цифр, чтобы однозначно идентифицировать внутреннее значение.(Другие не редкие правила - это преобразование в фиксированное количество цифр или преобразование в фиксированное количество цифр, а затем удаление конечных нулей. Как правило «как раз достаточно», так и правило «remoe-trailing-zeroes») дают результаты, показанные ввопрос. Я продемонстрирую достаточно справедливое правило в этом ответе.)

Значение, полученное с помощью 14/3, равно 4,66666666666666696272613990004174411296844482421875.Вместе со следующим более низким и следующим более представимым значениями мы имеем (с пробелом, вставленным после цифры 16 th , чтобы помочь визуализации):

4.666666666666666 0745477201999165117740631103515625
4.666666666666666 96272613990004174411296844482421875
4.666666666666667 850904559600166976451873779296875

Если мы конвертировали 4.666666666666667 вс плавающей точкой, какое из вышеуказанных значений должно быть результатом?Средний ближе;это всего около 0,04 (в единицах наименьшей цифры), в то время как остальные 0,93 и 0,15.Таким образом, 16 цифр «4.666666666666667» достаточно для однозначной идентификации 4.66666666666666696272613990004174411296844482421875.

Для сравнения рассмотрим 4/3, что составляет 1.33333333333333325931846502498956397175128893939.Это и два его соседа:

1.333333333333333 03727386009995825588703155517578125
1.333333333333333 2593184650249895639717578887939453125
1.333333333333333 481363069950020872056484222412109375

Опять же, пробел после 16 th цифры.Если бы мы конвертировали 16-значный 1.333333333333333 в число с плавающей точкой, какой из них должен быть результат?Теперь первый ближе;это всего 0,04 единиц.Таким образом, «1.333333333333333» не может представить правильное внутреннее значение.Нам нужен 17-значный «1.3333333333333333», чтобы однозначно идентифицировать желаемое значение.

Следующий случай:

λ> 34/3
11.333333333333334

Вопрос состоит в том, почему это «ошибочно округлено».не является.Внутреннее значение: 11,3333333333333339254522798000834882259368896484375.Это число и два соседних представимых им значения:

11.333333333333332149095440399833023548126220703125
11.3333333333333339254522798000834882259368896484375
11.33333333333333570180911920033395290374755859375

Среднее значение ближе всего к 11⅓, поэтому это правильный результат 34/3.И «11.333333333333334» - это правильное преобразование 11.3333333333333339254522798000834882259368896484375 в 17-значное десятичное число.

Аналогично, в:

λ> 44/3
14.666666666666666

Возможные результаты:

14.666666666666664 29819088079966604709625244140625
14.666666666666666 0745477201999165117740631103515625
14.666666666666667 850904559600166976451873779296875

Середина этого спроиграл 14⅔, так как он на расстоянии около .59 единиц (используя единицы в позиции, которую я пометил пробелом), тогда как последняя находится на расстоянии 1.18 единиц.Таким образом, правильный внутренний результат равен 14.6666666666666660745477201999165117740631103515625, а результат преобразования его в 17 десятичных цифр равен 14.666666666666666.

...