Почему ghci говорит, что 1.1 + 1.1 + 1.1> 3.3 Верно?

Я проходил учебное руководство Haskell недавно и заметил это поведение при попытке некоторых простых выражений Haskell в интерактивном ghci оболочка:

Prelude> 1.1 + 1.1 == 2.2
True
Prelude> 1.1 + 1.1 + 1.1 == 3.3
False
Prelude> 1.1 + 1.1 + 1.1 > 3.3
True
Prelude> 1.1 + 1.1 + 1.1
3.3000000000000003

Кто-либо знает, почему это?

8
задан Josh Lee 8 September 2011 в 20:59
поделиться

7 ответов

Поскольку 1,1 и 3.3 являются числа плавающих точек . Десятичные фракции, такие как .1 или .3, не совсем представлены в номере двоичной плавающей запятой. .1 означает 1/10. Представлять, что в двоичном двоике, где каждая фракционная цифра представляет 1/2 N (1/2, 1/4, 1/8 и т. Д.), вам понадобится бесконечное количество цифр, 0,000110011 ... повторяя бесконечно.

Это именно та же проблема, что и представляющая, скажем, 1/3 в базе 10. В базе 10 вам понадобится бесконечное количество цифр, .33333 ... Навсегда, чтобы точно представить 1/3. Так что работаю в базе 10, вы обычно раунд, к чему-то вроде .33. Но если вы добавите три копии этого, вы получаете .99, а не 1.

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

Для представления рациональных чисел точнее в Haskell, вы всегда можете использовать рациональный тип данных, соотношение ; В сочетании с Bignums (произвольно крупными целыми числами, в Haskell в Haskell, в отличие от INT , которые являются фиксированными размерами), как тип для числителя и знаменателя, вы можете представлять произвольные точные рациональные числа, но При значительно более медленной скорости, чем цифры плавающих точек, которые реализованы в оборудовании и оптимизированы для скорости.

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

36
ответ дан 5 December 2019 в 04:28
поделиться

.NET неправильно обрабатывает летнее время, несмотря на то, что дает нужные ответы. Вы хотите неверных ответов.

Краткая версия:

  • Как .NET может знать, что в 1977 году летнее время действовало весь год из-за энергетического кризиса?

  • Как .NET может знать, какими будут правила перехода на летнее время в Израиле, когда правила будут решаться кнессетом из года в год?

  • Как .NET знает, что США работали на летнее время круглый год во время Второй мировой войны, и с 1945 по 1966 годы правила DST варьировались в зависимости от региона, а правила по-прежнему варьировались в зависимости от региона.

.NET пытается выйти из системы и использует текущие правила перехода на летнее время, даже если они не действовали или будут действовать. В результате вы получаете ответы, которые, хотя вы думаете, что вы хотите, неверны.

Из записи блога Рэймонда Чена Почему переход на летнее время неинтуитивен :

Почему функции преобразования часовых поясов (win32) не используют часовой пояс, соответствующий времени года?

...

Win32 не пытается угадать, правила часового пояса действовали при этом в другой раз. Итак, Win32 говорит: " Четверг, 17 октября 2002 года 8:45:38 AM PST ".

Примечание: Тихоокеанское Стандартное время. Даже хотя 17 октября было во время Тихого океана Летнее время , Win32 отображает время как стандартное время, потому что это то, что время сейчас .

.NET говорит: " Ну, если правила в эффект теперь также был в силе на 17 октября 2003 года, тогда это будет дневное время ", поэтому оно отображается "Четверг, 17 октября 2003 года, 9 час. 45 мин. PDT "- летнее время.

Поэтому ответы, которые вы получите, неверны. Но так как вы ожидаете неправильного ответа, это то, что вы получаете.

-121--3383975-

Google Test рамка является альтернативой.

Вот простой пример из документации :

// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
  EXPECT_EQ(1, Factorial(0));
}

// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
  EXPECT_EQ(1, Factorial(1));
  EXPECT_EQ(2, Factorial(2));
  EXPECT_EQ(6, Factorial(3));
  EXPECT_EQ(40320, Factorial(8));
}

Он также хорошо играет с gmock , модельной структурой Google для C++.

-121--2297253-

Ошибки с плавающей запятой в Haskell можно избежать с помощью рациональных типов:

Prelude Data.Ratio> let a = (11 % 10) + (11 % 10) + (11 % 10)
Prelude Data.Ratio> a > (33 % 10)
False
Prelude Data.Ratio> fromRational a
3.3

Конечно, вы платите штраф за повышение точности.

13
ответ дан 5 December 2019 в 04:28
поделиться

выглядит как типичная проблема с плавающей точкой.

См. Что такое простой пример ошибки с плавающей точкой / округлением?

6
ответ дан 5 December 2019 в 04:28
поделиться

Это связано с тем, как работают номера плавающих точек IEEE.

1.1 представлен как 1,100000000000000001 в плавающей точке, 3.3 представлен как 3.2999999999999998.

Так что 1.1 + 1.1 + 1.1 на самом деле

1.100000000000000001 + 1.1000000000000001 + 1.1000000000000001 = 1.1000000000000001 = 3.300000000000000003

, который, как вы можете видеть, на самом деле больше 3.2999999999999998.

Обычный обходной путь - либо не оценивать равенство, либо для проверки, если число находится в целью +/- небольшой EPSILON (который определяет точность, которую нужно).

EX: если оба являются True, то сумма «равная» до 3,3 (в пределах допустимой ошибки).

1.1 + 1.1 + 1.1 < 3.3 + 1e9 
1.1 + 1.1 + 1.1 > 3.3 - 1e9
6
ответ дан 5 December 2019 в 04:28
поделиться

Немногие поплавки могут быть выражены точно с использованием представления IEEE 754, поэтому они всегда будут немного выключенный.

5
ответ дан 5 December 2019 в 04:28
поделиться

, потому что числа плавающих точек не являются точными (Википедия)

13
ответ дан 5 December 2019 в 04:28
поделиться

В общем, вы не следует сравнивать числа с плавающей запятой на равенство (по причинам, указанным выше). Единственная причина, по которой я могу думать, - это если вы хотите спросить: "Изменилось ли это значение?" Например, «if (newscore / = oldscore)», тогда предпримите какое-то действие. Это нормально до тех пор, пока вы не сравниваете результат двух отдельных вычислений, чтобы проверить, совпадают ли они (потому что тогда даже математически, если они равны, они могут округлить в противном случае).

1
ответ дан 5 December 2019 в 04:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: