Я проходил учебное руководство 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
Кто-либо знает, почему это?
Поскольку 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
, которые являются фиксированными размерами), как тип для числителя и знаменателя, вы можете представлять произвольные точные рациональные числа, но При значительно более медленной скорости, чем цифры плавающих точек, которые реализованы в оборудовании и оптимизированы для скорости.
Числа плавающих точек - это оптимизация, для научных и численных вычислений, которая компромит точность на высокую скорость, позволяя вам выполнить очень большое количество вычислений в небольшом времени, если вы знаете о округле и как это влияет на ваши вычисления.
.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
Конечно, вы платите штраф за повышение точности.
выглядит как типичная проблема с плавающей точкой.
См. Что такое простой пример ошибки с плавающей точкой / округлением?
Это связано с тем, как работают номера плавающих точек 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
Немногие поплавки могут быть выражены точно с использованием представления IEEE 754, поэтому они всегда будут немного выключенный.
, потому что числа плавающих точек не являются точными (Википедия)
В общем, вы не следует сравнивать числа с плавающей запятой на равенство (по причинам, указанным выше). Единственная причина, по которой я могу думать, - это если вы хотите спросить: "Изменилось ли это значение?" Например, «if (newscore / = oldscore)», тогда предпримите какое-то действие. Это нормально до тех пор, пока вы не сравниваете результат двух отдельных вычислений, чтобы проверить, совпадают ли они (потому что тогда даже математически, если они равны, они могут округлить в противном случае).