Сравнение двойных значений в С#

У меня есть double переменная, называемая x. В коде x получает значение 0.1 и я проверяю его в выражении "if", сравнивая x и 0.1

if (x==0.1)
{
----
}

К сожалению, он не вводит оператор if

  1. Должен ли я использовать Double или double?

  2. В чем причина этого? Можете ли вы предложить решение для этого?

Ответ 1

Это стандартная проблема из-за того, как компьютер сохраняет значения с плавающей запятой. Найдите здесь "проблему с плавающей точкой", и вы найдете массу информации.

Короче говоря, float/double не может точно хранить 0.1. Это будет всегда немного.

Вы можете попробовать использовать decimal тип, который хранит числа в десятичной нотации. Таким образом, 0.1 будет представиться точно.


Вы хотели знать причину:

Float/double хранятся как двоичные дроби, а не десятичные дроби. Проиллюстрировать:

12.34 в десятичной нотации (то, что мы используем) означает

1 * 101 + 2 * 100 + 3 * 10-1 + 4 * 10-2

Компьютер хранит числа с плавающей точкой таким же образом, за исключением того, что использует базу 2: 10.01 означает

1 * 21 + 0 * 20 + 0 * 2-1 + 1 * 2-2

Теперь вы, вероятно, знаете, что есть некоторые числа, которые не могут быть полностью представлены нашей десятичной нотацией. Например, 1/3 в десятичной нотации составляет 0.3333333… То же самое происходит в двоичной нотации, за исключением того, что числа, которые не могут быть представлены точно, различны. Среди них номер 1/10. В двоичной записи, которая составляет 0.000110011001100…

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

Ответ 2

double и double являются одинаковыми (double является псевдонимом для double) и может использоваться взаимозаменяемо.

Проблема сравнения двойника с другим значением состоит в том, что двойные числа являются приблизительными значениями, а не точными значениями. Поэтому, когда вы устанавливаете x в 0.1, он может быть сохранен в действительности как 0.100000001 или что-то в этом роде.

Вместо проверки на равенство вы должны убедиться, что разница меньше определенной минимальной разницы (допуска). Что-то вроде:

if (Math.Abs(x - 0.1) < 0.0000001)
{
    ...
}

Ответ 3

Вам нужна комбинация Math.Abs на X-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))

Ответ 4

Сравнение числа с плавающей запятой не всегда может быть выполнено именно из-за округления. Чтобы сравнить

(x == .1)

компьютер действительно сравнивает

(x - .1) vs 0

Результат sybtraction не всегда может быть репрезентален именно из-за того, что количество чисел с плавающей запятой представлено на машине. Поэтому вы получаете некоторое ненулевое значение, а условие оценивается как false.

Чтобы преодолеть это сравнение

Math.Abs(x- .1) vs some very small threshold ( like 1E-9)

Ответ 5

Из документации:

Точность в сравнениях. Метод Equals следует использовать с осторожностью, поскольку два явно эквивалентных значения могут быть неодинаковыми из-за различной точности двух значений. В следующем примере показано, что значение Double.3333 и значение Double, возвращаемые делением 1 на 3, являются неравными.

...

Вместо того, чтобы сравнивать на равенство, один рекомендуемый метод включает определение приемлемого запаса разницы между двумя значениями (например, 0,01% от одного из значений). Если абсолютное значение разности между этими двумя значениями меньше или равно этому пределу, разница, вероятно, будет связана с различиями в точности и, следовательно, значения, вероятно, будут равны. В следующем примере этот метод используется для сравнения .33333 и 1/3, двух значений Double, которые в предыдущем примере кода оказались неравными.

Так что если вам действительно нужен дубль, вы должны использовать технику, описанную в документации. Если можете, измените его на десятичное. Это будет медленнее, но у вас не будет проблем такого типа.

Ответ 6

Двойные и двойные идентичны.

По этой причине см. http://www.yoda.arachsys.com/csharp/floatingpoint.html. Короче говоря, двойной тип не является точным, а разница в минутах между "x" и "0.1" отбрасывает его.

Ответ 7

Точное сравнение значений с плавающей запятой известно, что не всегда работает из-за проблемы округления и внутреннего представления.

Попробуйте неточное сравнение:

if (x >= 0.099 && x <= 0.101)
{
}

Другой альтернативой является использование десятичного типа данных.

Ответ 8

Используйте decimal. У этого нет этой "проблемы".

Ответ 9

1) Должен ли я использовать Double или double???

Double и Double - одно и то же. Double - это просто ключевое слово С#, работающее как псевдоним для класса System.Double Самое распространенное - использовать псевдонимы! То же самое для string (System.String), int (System.Int32)

Также см. Таблица встроенных типов (ссылка на С#)

Ответ 10

Двойной (называемый float на некоторых языках) является проблемой с проблемами из-за проблем округления, это хорошо, только если вам нужны приблизительные значения.

Тип данных Decimal делает то, что вы хотите.

Для ссылочных десятичных и десятичных одинаковых в .NET С#, как и двойные и двойные типы, оба они относятся к одному типу (десятичные и двойные очень разные, хотя, как вы видели).

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

Ответ 11

Взяв подсказку из базы Java-кода, попробуйте использовать .CompareTo и проверьте нулевое сравнение. Это предполагает, что функция .CompareTo принимает точный учет равенства с плавающей запятой. Например,

System.Math.PI.CompareTo(System.Math.PI) == 0

Этот предикат должен возвращать true.

Ответ 12

Представления чисел с плавающей запятой общеизвестно неточны (из-за способа хранения чисел с плавающей запятой), например, x может фактически быть 0.0999999999 или 0.100000001, и ваше условие не будет выполнено. Если вы хотите определить, равны ли значения с плавающей точкой, вам нужно указать, равны ли они в пределах определенного допуска.

т.е.

if(Math.Abs(x) - 0.1 < tol) {
   // Do something 
}

Ответ 13

Как правило:

Двойное представление достаточно хорошо в большинстве случаев, но в некоторых ситуациях может с трудом потерпеть неудачу. Используйте десятичные значения, если вам нужна полная точность (как в финансовых приложениях).

Большинство проблем с двойниками исходит не из прямого сравнения, а из-за накопления нескольких математических операций, которые экспоненциально нарушают значение из-за округления и дробных ошибок (особенно с умножениями и делениями).

Проверьте свою логику, если код:

x = 0.1

if (x == 0.1)

он не должен терпеть неудачу, он просто проваливается, если значение X вычисляется более сложными средствами или операциями, вполне возможно, что метод ToString, используемый отладчиком, использует интеллектуальное округление, возможно, вы можете сделать то же самое (если это слишком рискованно вернуться к использованию десятичной дроби):

if (x.ToString() == "0.1")

Ответ 14

Классный hack, который я нашел, - это использовать метод .GetHashCode(), который возвращает int, представляющий двойную, т.е.

(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

while: (0.4d + 0.3d + 0.2d + 0.1d) == 1d //returns false

Я попробовал его в нескольких случаях и, похоже, хорошо работает.

Ответ 15

// number of digits to be compared    
public int n = 12 
// n+1 because b/a tends to 1 with n leading digits
public double Epsilon { get; } = Math.Pow(10, -(n+1)); 

public bool IsEqual(double a, double b)
{
    if (Math.Abs(a)<= double.Epsilon || Math.Abs(b) <= double.Epsilon)
        return Math.Abs(a - b) <=  double.Epsilon;
    return Math.Abs(1.0 - a / b) <=  Epsilon;
}

Ответ 16

Большинство способов или способов глупого расширения!

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;
}