Правильний спосіб порівняти систему. Подвоїти до "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. Дуже багато значень. Чому б просто не перевірити це і не переконатися на власні очі?
Ігбі Ларгеман,

12
Я просто написав те саме, що написав Решарпер, щоб показати, де я зосереджений.
radbyx

@Charles: Крім того, є багато цифр, менших за 0.
Брайан

Відповіді:


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
}

Редагувати : цей код помилковий. Епсилон - це найменше число, але не зовсім нульове. Коли ви хочете порівняти число з іншим числом, вам слід подумати, яка допустима толерантність. Скажімо, що все, що перевищує .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 без використання ==. Принаймні це робить сенс математично. Я припускаю, що у вас є подвійний, і ви хочете порівняти його з поняттям нуля без ==. Якщо ваш дубль відрізняється від 0d з будь-якої причини, включаючи округлення, тестова заливка повертає false. Це порівняння здається дійсним для будь-якого подвійного і поверне істину лише в тому випадку, якщо цей подвійний є найменшим за найменше число, яке можна представити, що здається хорошим визначенням для тестування концепції 0, чи не так?
sonatique

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) і intправий бік (rhs).

Але зараз здається, що ви думаєте, що lhs буде перетворено на int, а потім ==знак порівняє два цілих числа. Це НЕ то , що відбувається. Перетворення з double на int є явним і не може відбуватися "автоматично".

Натомість трапляється навпаки. Rhs перетворюється на double, і тоді ==знак стає тестом на рівність двох подвійних. Це перетворення є неявним (автоматичним).

Вважається кращим (дехто) писати

if (something == 0.0)

або

if (something == 0d)

бо тоді одразу ж ви порівнюєте два дублі. Однак це лише питання стилю та читабельності, оскільки компілятор у будь-якому випадку зробить те саме.

У деяких випадках також доречно ввести "толерантність", як у відповіді Джона Скіта, але ця толерантність теж була б double. Звичайно, це може бути, 1.0якщо ви хочете, але це не повинно бути [найменш суворо позитивним] цілим числом.


17

Якщо ви просто хочете придушити попередження, зробіть наступне:

if (something.Equals(0.0))

Звичайно, це дійсне рішення, лише якщо ви знаєте, що дрейф не турбує. Я часто роблю це, щоб перевірити, чи збираюся ділити на нуль.


4

Я не думаю, що це рівно, чесно. Розглянемо власний приклад yuor: щось = 0,9 або 0,0004. У першому випадку це буде НЕВИЩИМ, у другому випадку - ІСТИННИМ. Займаючись цим типом, я зазвичай визначаю для себе відсоток точності і порівнюю в межах цієї точності. Залежить від ваших потреб. щось на зразок...

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


//do something
}

Сподіваюся, це допомагає.


2
щось має бути рівним нулю.
radbyx

отже, вам щасливий хлопець, в даному випадку :)
Тігран

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.