Вопрос

Я столкнулся со следующей арифметической задачей.

Но результат отличается от обычной математической операции. Почему?

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, и, следовательно, многие десятичные числа с плавающей запятой не могут быть точно представлены ограниченным количеством цифр.

Потому что вы используете числа с плавающей запятой.

http://docs.sun.com/source/806-3568/ncg_goldberg.html

Если вы выполняете такую ​​арифметику в своем приложении, 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: проблема одинакова для обоих:

http://docs.python.org/tutorial/floatingpoint.html

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top