Перейти к содержимому

Как правильно сравнивать double в c

  • автор:

Double.CompareTo Method in C# with Examples

Double.CompareTo Method is used to compare the current instance to a specified object or Double object. It will return an integer which shows whether the value of the current instance is greater than, less than or equal to the value of the specified object or Double object. There are 2 methods in the overload list of this method as follows:

Double.CompareTo(Double) Method

Double.CompareTo() Method is used to compare the current instance to a specified double-precision floating-point number and returns an integer which shows whether the value of this instance is less than, equal to, or greater than the value of the specified double-precision floating-point number.

Syntax:

Here, it takes a double-precision floating-point number to compare.

Return Value: This method returns a signed number indicating the relative values of this instance and value.

  • Less than zero: This instance is less than value or this instance is not a number (NaN) and value is a number.
  • Zero: This instance is equal to value or both this instance and value are not a number (NaN), PositiveInfinity, or NegativeInfinity.
  • Greater than zero: This instance is greater than value or this instance is a number and value is not a number (NaN).

Below programs illustrate the use of Double.CompareTo(Double) Method:

Example 1:

Example 2:

Double.CompareTo(Object) Method

Double.CompareTo() Method is used to compare the current instance to a specified object and returns an integer which indicates whether the value of the current instance is less than, equal to, or greater than the value of the specified object.

Syntax:

Here, it takes an object to compare, or null.

Return Value: This method returns a signed number indicating the relative values of this instance and value.

  • Less than zero: This instance is less than value or this instance is not a number (NaN) and value is a number.
  • Zero: This instance is equal to value or both this instance and value are not a number (NaN), PositiveInfinity, or NegativeInfinity.
  • Greater than zero: This instance is greater than value or this instance is a number and value is not a number (NaN).

Exception: It throws ArgumentException if value is not a Double.

Comparing Floating Point Numbers in C/C++

Elliot Chance

Comparing floating point numbers for equality can be problematic. It’s difficult because often we are comparing small or large numbers that are not represented exactly. There is also issues with rounding errors caused by not being able to represent and exact value.

Rather than doing a strict value comparison ( == ), we treat two values as equal if there values are very close to each other. So what does “very close” mean? Well to answer that we have to take a look at how the numbers are represented in memory.

Here is an example 32bit float:

And here is the layout of a double precision (64bit) float:

The number of bits in the fraction can be thought of as the number of significant bits (or, accuracy) of the number. We do not want to use all of the fraction bits otherwise we would be doing a strict comparison, but we will use most. For both sized float types I will use 4 less significant bits:

We will use this to calculate the epsilon (the small difference that is still considered equal). However, we have to be careful that the number of bits that we use to calculate the epsilon is based on the smallest precision value in the comparison. For example is we compare a 32bit float with a 64bit float we must use only the precision of the 32bit float.

How to compare two double values in C#

Many candidates are rejected or down-leveled in technical interviews due to poor performance in behavioral or cultural fit interviews. Ace your interviews with this free course, where you will practice confidently tackling behavioral interview questions.

Overview

We use the CompareTo() method to compare two double values or objects in Ruby. Remember that a double value in C# represents a double-precision, floating-point number.

When we compare two double values, say d1 and d2 , the CompareTo() method returns an integer that is less than 1 if the latter is lesser than the former. It returns 0 if they are both equal, or just 1 if the latter is greater.

5.6 – Операторы отношения и сравнение чисел с плавающей запятой

Операторы отношения – это операторы, позволяющие сравнивать два значения. Существует 6 операторов отношения:

Операторы отношения

Оператор Обозначение Пример использования Операция
Больше > x > y true , если x больше, чем y ; в противном случае – false
Меньше < x < y true , если x меньше, чем y ; в противном случае – false
Больше или равно >= x >= y true , если x больше или равно y ; в противном случае – false
Меньше или равно <= x <= y true , если x меньше или равно y ; в противном случае – false
Равно == x == y true , если x равно y ; в противном случае – false
Не равно != x != y true , если x не равно y ; в противном случае – false

Вы уже видели, как работает большинство из них, и они интуитивно понятны. Каждый из этих операторов возвращает логическое значение true (1) или false (0).

Вот пример кода, использующего эти операторы с целочисленными значениями:

И результаты пробного запуска:

При сравнении значений целочисленных типов эти операторы чрезвычайно просто использовать.

Логические условные значения

По умолчанию условия в инструкции if или условном операторе (и некоторых других местах) оцениваются как логические значения.

Многие начинающие программисты могут писать подобные инструкции:

Это избыточно, поскольку == true фактически не добавляет никакого значения условию. Вместо этого мы должны написать:

лучше записать как:

Лучшая практика

Не добавляйте в условия ненужные == или != . Это затрудняет чтение, не предлагая никакой дополнительной ценности.

Сравнение значений с плавающей запятой

Рассмотрим следующую программу:

Обе переменные d1 и d2 должны иметь значение 0,01. Но эта программа выдает неожиданный результат:

Если вы проверите значения d1 и d2 в отладчике, вы, вероятно, увидите, что d1 = 0,0100000000000005116, а d2 = 0,0099999999999997868. Оба числа близки к 0,01, но d1 больше, а d2 меньше.

Если требуется высокий уровень точности, сравнение значений с плавающей запятой с использованием любого из операторов отношения может быть опасным. Это связано с тем, что значения с плавающей запятой неточны, а небольшие ошибки округления в операндах с плавающей запятой могут привести к неожиданным результатам. Если вам нужно напомнить, то мы обсуждали ошибки округления в уроке «4.8 – Числовые типы с плавающей точкой».

Когда операторы «меньше» и «больше» ( < , <= , > и >= ) используются со значениями с плавающей запятой, они обычно дают правильный ответ (потенциальный сбой возникает, только если операнды почти идентичны). Из-за этого использование этих операторов с операндами с плавающей запятой может быть приемлемым, если последствия получения неправильного ответа при схожих операндах незначительны.

Например, рассмотрим игру (такую ​​как Space Invaders), в которой вы хотите определить, пересекаются ли два движущихся объекта (например, ракета и инопланетянин). Если объекты всё еще находятся далеко друг от друга, эти операторы вернут правильный ответ. Если два объекта расположены очень близко друг к другу, вы можете получить любой ответ. В таких случаях неправильный ответ, вероятно, даже не будет замечен (это будет просто выглядеть как промах или попадание), и игра продолжится.

Равенство значений с плавающей запятой

Операторы равенства ( == и != ) намного сложнее. Рассмотрим оператор == , который возвращает истину, только если его операнды в точности равны. Поскольку даже самая маленькая ошибка округления приведет к тому, что два числа с плавающей запятой не будут равны, operator== имеет высокий риск возврата false , когда можно было бы ожидать true . У оператора != такая же проблема.

По этой причине следует избегать использования этих операторов с операндами с плавающей запятой.

Предупреждение

Избегайте использования операторов operator== и operator!= с операндами с плавающей запятой.

Итак, как мы можем корректно сравнить два операнда с плавающей запятой, чтобы увидеть, равны ли они?

Самый распространенный метод обеспечения равенства с плавающей запятой включает использование функции, которая проверяет, почти ли равны два числа. Если они «достаточно близки», то мы называем их равными. Значение, используемое для обозначения «достаточно близко», традиционно называется эпсилон. Эпсилон обычно определяется как небольшое положительное число (например, 0,00000001, иногда пишется 1e-8).

Начинающие разработчики часто пытаются написать свою собственную функцию «достаточно близки» следующим образом:

std::abs() – это функция в заголовке <cmath> , которая возвращает абсолютное значение (модуль) своего аргумента. Итак, std::abs(a — b) <= epsilon проверяет, меньше ли расстояние между a и b , чем любое значение epsilon , означающее «достаточно близко», которое было передано в функцию. Если a и b достаточно близки, функция возвращает true , чтобы указать, что они равны. В противном случае возвращается false .

Хотя эта функция может работать, но она не очень хороша. Эпсилон 0.00001 подходит для входных значений около 1.0 , но слишком велико для входных значений около 0.0000001 и слишком мало для входных значений, таких как 10000 . Это означает, что каждый раз, когда мы вызываем эту функцию, мы должны выбирать эпсилон, подходящий для наших входных данных. Если мы знаем, что нам придется масштабировать эпсилон пропорционально нашим входным данным, мы могли бы также изменить функцию, чтобы она делала это за нас.

Дональд Кнут, предложил следующий метод в своей книге «Искусство программирования, том 2: Получисленные алгоритмы»:

В этом случае, вместо абсолютного значения, эпсилон теперь зависит от величины a или b .

Давайте рассмотрим подробнее, как работает эта выглядящая безумно функция. Левая часть оператора <= (т.е. std::abs(a — b) ) сообщает нам расстояние между a и b как положительное число.

В правой части оператора <= нам нужно вычислить наибольшее значение «достаточно близко», которое мы готовы принять. Для этого алгоритм выбирает большее из a и b (в качестве приблизительного показателя общей величины чисел), а затем умножает его на эпсилон. В этой функции эпсилон представляет собой процент. Например, если мы хотим сказать «достаточно близко» означает, что a и b находятся в пределах 1% от большего из a и b , мы передаем эпсилон 0.01 (1% = 1/100 = 0,01). Значение эпсилон может быть изменено на любое, наиболее подходящее для обстоятельств (например, эпсилон 0.002 означает в пределах 0,2%).

Чтобы выполнить сравнение на неравенство ( != ) вместо равенства, просто вызовите эту функцию и используйте оператор логического НЕ ( ! ), чтобы инвертировать результат:

Обратите внимание, что хотя функция approximatelyEqual() в большинстве случаев будет работать, она не идеальна, особенно когда числа близки к нулю:

Возможно, это будет сюрпризом, но программа напечатает следующее:

Второй вызов не сработал, как ожидалось. Наши расчеты сломались при приближении почти до нуля.

Один из способов избежать этого – использовать как абсолютный эпсилон (как мы делали в первом подходе), так и относительный эпсилон (как мы делали в подходе Кнута):

В этом алгоритме мы сначала проверяем, близки ли a и b друг к другу в абсолютном выражении, что обрабатывает случай, когда a и b оба близки к нулю. Параметр absEpsilo n должен быть установлен на очень маленькое значение (например, 1e-12). Если это не удается, мы возвращаемся к алгоритму Кнута, используя относительный эпсилон.

Вот наш предыдущий код, проверяющий оба алгоритма:

Вы можете видеть, что при правильно подобранном absEpsilon функция approximatelyEqualAbsRel() правильно обрабатывает маленькие входные значения.

Сравнение чисел с плавающей запятой – сложная тема, и не существует универсального алгоритма, подходящего для всех случаев. Тем не менее, функция approximatelyEqualAbsRel() должна достаточно хорошо подходить для обработки большинства случаев, с которыми вы столкнетесь.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *