Почему для этой задачи результат другой?
-
05-09-2019 - |
Вопрос
Я столкнулся со следующей арифметической задачей.
Но результат отличается от обычной математической операции. Почему?
double d1 = 1.000001;
double d2 = 0.000001;
Console.WriteLine((d1-d2)==1.0);
Решение
Полагаю, вы нашли этот вопрос на странице «головоломки» Джона Скита?Ответы перечислены и объяснены. здесь на том же сайте.
Для справки, вот ответ, скопированный с этой страницы.
3) Глупая арифметика
Компьютеры созданы для того, чтобы хорошо справляться с арифметикой, не так ли?Почему это печатает «False»?
double d1 = 1.000001; double d2 =
0.000001; Console.WriteLine((d1-d2)==1.0);
Отвечать:Все значения здесь хранятся в виде двоичных чисел с плавающей запятой.В то время как 1.0 можно хранить точно, 1.000001 на самом деле хранится как 1.000000999999999999177333620536956004798412322998046875, и 0.000001 фактически хранится как 0.00000099999999999999999954748111825886258685613938723690807819366455078125.Разница между ними не равна ровно 1,0, и на самом деле разницу также невозможно сохранить точно.
Другие советы
Из записи MSDN для Двойной.Равно:
Точность в сравнениях
Метод Equals следует использовать с Осторожно, потому что два по-видимому эквивалентные значения могут быть неравными из-за к разной точности двух ценности.Следующие примеры отчетов что двойное значение .3333 и Двойной возвращается делением 1 на 3 неравный.
...
Вместо того, чтобы сравнивать для равенства, один рекомендуемый метод включает в себя определение приемлемого предела разница между двумя значениями (такими как .01% от одного из значений).Если абсолютное значение разницы между двумя значениями меньше или равна этой марже, разница Meme it вероятно, из-за различий в точность и, следовательно, значения вероятно, равны.Следующие пример использует эту технику для сравнения .33333 и 1/3, два двойных значения что предыдущий пример кода нашел быть неравным.
Если вам нужно выполнить много сравнений «на равенство», возможно, было бы неплохо написать небольшую вспомогательную функцию или метод расширения в .NET 3.5 для сравнения:
public static bool AlmostEquals(this double double1, double double2, double precision)
{
return (Math.Abs(double1 - double2) <= precision);
}
Это можно использовать следующим образом:
double d1 = 1.000001;
double d2 = 0.000001;
bool equals = (d1 - d2).AlmostEquals(1.0, 0.0000001);
Посмотрите этот очень похожий вопрос: С#.NET:Безопасно ли проверять значения с плавающей запятой на равенство 0?
Это связано с тем, что компьютеры выполняют математические операции по основанию 2, и, следовательно, многие десятичные числа с плавающей запятой не могут быть точно представлены ограниченным количеством цифр.
Потому что вы используете числа с плавающей запятой.
Если вы выполняете такую арифметику в своем приложении, decimal
следует использовать тип
decimal d1 = 1.000001M;
decimal d2 = 0.000001M;
Console.WriteLine((d1 - d2) == 1.0M); // evaluates as true
это может быть связано с проблемами точности с плавающей запятой, поскольку ваш результат может быть не совсем 1,0, а может быть что-то вроде 1,000000000001
Это происходит потому, что типы с плавающей запятой хранят числа, используя представление по основанию два, а не по основанию десять.Это приводит к тому, что double не может точно хранить значения типа 0,1.Например, 0,1 представлено одним значением 0,100000001490116119384765625.
Вам нужно использовать Decimal, чтобы избавиться от ошибки.
Это связано с тем, как числа с плавающей запятой работают в процессоре, а не только в C#.Видеть этот Запись в Википедии и статья здесь для получения дополнительной информации.
Короткий ответ: числа с плавающей запятой не сохраняются как точное представление, поэтому сравнение с использованием «==" не работает так, как вы пытаетесь его использовать.
Посмотрите, что говорят документы Python: проблема одинакова для обоих: