Сравнение двойных значений в C#
у меня есть double переменная x.
В коде x присваивается значение 0.1 и я проверяю его в выражении "если", сравнивая x и 0.1
if (x==0.1)
{
----
}
к сожалению, он не входит в if сообщении
я должен использовать
Doubleилиdouble?в чем причина этого? Можете ли вы предложить решение для этого?
15 ответов:
это стандартная проблема из-за того, как компьютер хранит числа с плавающей точкой. Поиск здесь для "Проблемы с плавающей точкой", и вы найдете тонны информации.
короче-поплавок / двойной не может хранить
0.1точно. Он всегда будет немного.вы можете попробовать использовать
decimalтип, который хранит числа в десятичной системе счисления. Таким образом0.1будет представимо точно.
вы хотели знать причина:
Float / double хранятся в виде двоичных дробей, а не десятичных дробей. Для иллюстрации:
12.34в десятичной системе счисления (что мы используем) означает1 * 101 + 2 * 100 + 3 * 10-1 + 4 * 10-2компьютер хранит числа с плавающей запятой таким же образом, за исключением того, что он использует base
2:10.01означает1 * 21 + 0 * 20 + 0 * 2-1 + 1 * 2-2теперь вы, вероятно, знаете, что есть некоторые числа, которые не могут быть представлены полностью с нашей десятичной нотацией. Например,
1/3в десятичной записи0.3333333…. То же самое происходит в двоичной системе счисления, за исключением того, что цифры, которые не могут быть представлены точно разные. Среди них есть число1/10. В двоичной системе счисления это0.000110011001100….поскольку двоичная нотация не может хранить его точно, он хранится в округленном виде. Отсюда и ваша проблема.
doubleиDoubleто же самое (doubleпсевдонимDouble) и может использоваться взаимозаменяемо.проблема с сравнение двойное с другим значением заключается в том, что двойники являются приблизительными, а не точными значениями. Поэтому, когда вы установите
xдо0.1на самом деле он может храниться как0.100000001или что-то подобное.вместо проверки равенства, вы должны проверить, что разница меньше, чем определенная минимальная разница (допуск). Что-то например:
if (Math.Abs(x - 0.1) < 0.0000001) { ... }
вам нужно сочетание
Math.AbsonX-Yиvalueсравнивать.вы можете использовать следующий метод расширения подход
public static class DoubleExtensions { const double _3 = 0.001; const double _4 = 0.0001; const double _5 = 0.00001; const double _6 = 0.000001; const double _7 = 0.0000001; public static bool Equals3DigitPrecision(this double left, double right) { return Math.Abs(left - right) < _3; } public static bool Equals4DigitPrecision(this double left, double right) { return Math.Abs(left - right) < _4; } ...так как вы редко вызываете методы на double кроме
ToStringЯ считаю его довольно безопасным расширения.затем вы можете сравнить
xиyкак
if(x.Equals4DigitPrecision(y))
сравнение числа с плавающей запятой не всегда может быть сделано именно из-за округления. Для сравнения
(x == .1)компьютер действительно сравнивает
(x - .1) vs 0результат sybtraction не всегда может быть представлен именно из-за того, как число с плавающей запятой представлены на машине. Поэтому вы получаете некоторое ненулевое значение и условие оценивается в
false.чтобы преодолеть это сравнение
Math.Abs(x- .1) vs some very small threshold ( like 1E-9)
с документация:
точность в сравнении Метод Equals следует использовать с осторожностью, поскольку два явно эквивалентных значения могут быть неравными из-за различной точности этих двух значений. В следующем примере сообщается, что значение Double .3333 и двойник, возвращенный делением 1 на 3, неравны.
...
вместо сравнения для равенства, один рекомендуемый метод включает в себя определение допустимой границы разницы между двумя значениями (например .01% от одного из значений). Если абсолютное значение разницы между этими двумя значениями меньше или равно этому пределу, то разница, вероятно, будет обусловлена различиями в точности и, следовательно, значения, вероятно, будут равны. В следующем примере этот метод используется для сравнения .33333 и 1/3, два двойных значения, которые в предыдущем примере кода оказались неравными.
Так что если вам действительно нужен двойник, вы должны использовать технологию, описанную в документации. Если вы можете, измените его на десятичный. это будет медленнее, но у вас не будет такого типа проблем.
Double и double идентичны.
по этой причине см. http://www.yoda.arachsys.com/csharp/floatingpoint.html . Короче говоря: двойной не является точным типом, и минутная разница между "x" и "0.1" отбросит его.
точное сравнение значений с плавающей запятой, как известно, не всегда работает из-за проблемы округления и внутреннего представления.
попробуйте неточное сравнение:
if (x >= 0.099 && x <= 0.101) { }другой альтернативой является использование типа данных decimal.
использовать
decimal. У него нет этой "проблемы".
1) Должен ли я использовать Double или double???
Doubleиdoubleэто одно и то же.double- это просто ключевое слово C# работает как псевдоним для классаSystem.DoubleСамое распространенное - использовать псевдонимы! То же самое дляstring(System.String),int(System.Int32)
Double (называемый float на некоторых языках) является fraut с проблемами из-за округления вопросов, это хорошо, только если вам нужны приблизительные значения.
десятичный тип данных делает то, что вы хотите.
для ссылки decimal и Decimal одинаковы в .NET C#, как и типы double и Double, они оба относятся к одному и тому же типу (decimal и double очень разные, хотя, как вы видели).
помните, что десятичный тип данных имеет некоторые затраты, связанные с ним, так что используйте его с осторожностью, если вы смотрите на петли и т. д.
представления чисел с плавающей запятой заведомо неточны (из-за того, как поплавки хранятся внутри), например, x может быть 0.099999999 или 0.100000001, и ваше условие не будет выполнено. Если вы хотите определить, равны ли поплавки, вам нужно указать, равны ли они в пределах определенного допуска.
т. е.
if(x - 0.1 < tol)
Как правило:
двойное представление достаточно хорошо в большинстве случаев, но может с треском провалиться в некоторых ситуациях. Использовать десятичные значения, если вам нужна полная точность (как в финансовых приложениях).
большинство проблем с двойниками не происходит от прямого сравнения, оно используется как результат накопления нескольких математических операций, которые экспоненциально нарушают значение из-за округления и дробных ошибок (особенно с умножениями и отделы.)
проверьте свою логику, если код:
x = 0.1 if (x == 0.1)он не должен терпеть неудачу, он должен просто терпеть неудачу, если значение X вычисляется более сложными средствами или операциями, вполне возможно, что метод ToString, используемый отладчиком, использует интеллектуальное округление, возможно, вы можете сделать то же самое (если это слишком рискованно, вернитесь к использованию decimal):
if (x.ToString() == "0.1")
классный Хак я нашел, чтобы использовать
.GetHashCode()метод, который возвращает int, представляющий double, т. е.
(0.4d + 0.3d + 0.2d + 0.1d).GetHashCode() //returns -1072693248
1d.GetHashCode() //returns 1072693248Итак, как вы уже заметили, мы можем использовать что-то вроде этого
public static bool AccurateEquality(double first,double second) { return Math.Abs(first.GetHashCode()) == Math.Abs(second.GetHashCode()); }использование :
AccurateEquality((0.4d + 0.3d + 0.2d + 0.1d),1) //returns trueвремя :
(0.4d + 0.3d + 0.2d + 0.1d) == 1d //returns falseЯ пробовал это на нескольких случаях, и это, кажется, работает хорошо.
принимая подсказку из базы кода Java, попробуйте использовать
.CompareToи тест для нулевого сравнения. Это предполагает.CompareToфункция учитывает равенство с плавающей запятой точным образом. Например,System.Math.PI.CompareTo(System.Math.PI) == 0этот предикат должен возвращать
true.
большинство выше способов или следующий глупый метод расширения!
public static bool EqualsTo(this double value, double value2) { var bytes1 = BitConverter.GetBytes(value); var bytes2 = BitConverter.GetBytes(value2); var long1 = BitConverter.ToInt64(bytes1, 0); var long2 = BitConverter.ToInt64(bytes2, 0); return long1 == long2; }
Comments