Правильный способ сравнить System.Double с '0' (числом, int?)


87

Извините, это может быть простой глупый вопрос, но мне нужно знать, чтобы быть уверенным.

У меня есть это ifвыражение,

void Foo()
{
    System.Double something = GetSomething();
    if (something == 0) //Comparison of floating point numbers with equality 
                     // operator. Possible loss of precision while rounding value
        {}
}

Это выражение равно

void Foo()
{
    System.Double something = GetSomething();
    if (something < 1)
        {}
}

? Потому что тогда у меня может возникнуть проблема, если ввести, ifнапример, значение 0,9.


3
// Comparison of floating point numbers with equality // operator. Вам действительно нужно было это указать? :)
Джордж Джонстон

1
Черт возьми нет. Существует чертовски много значений от 0 до 1. Почему бы просто не проверить это и убедиться в этом?
Igby Largeman

12
Я просто написал то же самое, что и Решарпер, чтобы показать, на чем я сосредоточен.
radbyx 06

@Charles: Кроме того, есть много чисел меньше нуля,
Брайан

Ответы:


115

Ну, насколько близко вам нужно, чтобы значение было 0? Если вы выполняете множество операций с плавающей запятой, которые при «бесконечной точности» могут привести к 0, вы можете получить результат «очень близкий» к 0.

Обычно в этой ситуации вы хотите предоставить какой-то эпсилон и проверить, что результат находится именно в этом эпсилоне:

if (Math.Abs(something) < 0.001)

Эпсилон, который вы должны использовать, зависит от приложения - это зависит от того, что вы делаете.

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


На самом деле, мне нужно, чтобы он был ровно нулевым, как бы это выглядело? Я искал Double.Zero, но знаю удачу. Есть постоянное право? Кстати, спасибо, теперь я получил
эпсилонскую

24
@radbyx: Просто используйте == 0. У вас есть буквальное значение - это довольно постоянно :)
Джон Скит

35

Если somethingон был назначен в результате другой операции, чем something = 0тогда, вам лучше использовать:

if(Math.Abs(something) < Double.Epsilon)
{
//do something
}

Изменить : этот код неверен. Эпсилон - наименьшее число, но не совсем ноль. Если вы хотите сравнить одно число с другим числом, вам нужно подумать о том, каков допустимый допуск. Допустим, вам все равно, что больше 0,00001. Это номер, который вы бы использовали. Значение зависит от домена. Однако Double.Epsilon, как правило, никогда не бывает.


2
Это не решает проблему округления, например , Math.Abs(0.1f - 0.1d) < double.Epsilonэтоfalse
Томас Lule

6
Double.Epsilon слишком мал для такого сравнения. Double.Epsilon - это наименьшее положительное число, которое может представлять double.
Евгений Набоков

3
В этом нет смысла, потому что это практически то же самое, что сравнивать с 0. -1
Гаспа79

1
Цель состоит в том, чтобы сравнить с концепцией 0 без использования ==. По крайней мере, математически это разумно. Я предполагаю, что у вас есть двойник под рукой, и вы хотите сравнить его с концепцией нуля без ==. Если ваш double отличается от 0d по какой-либо причине, включая округление, тестовое заполнение вернет false. Это сравнение кажется верным для любого типа double и вернет истину только в том случае, если это двойное число меньше наименьшего числа, которое может быть представлено, что кажется хорошим определением для проверки концепции 0, не так ли?
сонатика 01

3
@MaurGi: ты ошибаешься: double d = Math.Sqrt(10100)*2; double a = Math.Sqrt(40400); if(Math.Abs(a - d) < double.Epsilon) { Console.WriteLine("true"); }
sonatique

26

Ваш something- это double, и вы правильно определили, что в строке

if (something == 0)

у нас есть doubleслева (lhs) и an intсправа (rhs).

Но теперь кажется, что вы думаете, что lhs будет преобразовано в int, а затем ==знак будет сравнивать два целых числа. Это не то, что происходит. Преобразование из double в int является явным и не может происходить «автоматически».

Вместо этого происходит обратное. Правая часть преобразуется в double, а затем ==знак становится проверкой равенства между двумя двойными числами. Это преобразование неявное (автоматическое).

Некоторые считают, что лучше написать

if (something == 0.0)

или

if (something == 0d)

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

В некоторых случаях также уместно ввести «толерантность», как в ответе Джона Скита, но эта толерантность тоже будет double. Конечно, может быть, 1.0если вы захотите, но это не обязательно должно быть [наименее строго положительное] целое число.


17

Если вы просто хотите подавить предупреждение, сделайте следующее:

if (something.Equals(0.0))

Конечно, это верное решение, только если вы знаете, что дрейф не имеет значения. Я часто делаю это, чтобы проверить, не собираюсь ли делить на ноль.


4

Честно говоря, я не думаю, что это равнозначно. Рассмотрим ваш собственный пример: something = 0,9 или 0,0004. В первом случае это будет ЛОЖЬ, во втором - ИСТИНА. Имея дело с этими типами, я обычно определяю процент точности и сравниваю с этой точностью. Зависит от ваших потребностей. что-то типа...

if(((int)(something*100)) == 0) {


//do something
}

Надеюсь это поможет.


2
что-то должно быть ровно нулем.
radbyx 06

так что ты везунчик, в данном случае :)
Тигран

3

Вот пример, представляющий проблему (подготовленный в LinQPad - если у вас его нет, просто используйте Console.Writelineвместо Dumpметода):

void Main()
{
    double x = 0.000001 / 0.1;
    double y = 0.001 * 0.01; 

    double res = (x-y);
    res.Dump();
    (res == 0).Dump();
}

И x, и y теоретически одинаковы и равны: 0,00001, но из-за отсутствия «бесконечной точности» эти значения немного отличаются. К сожалению, достаточно немного, чтобы вернуться falseпри сравнении с 0 обычным способом.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.