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



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



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


к сожалению, он не входит в if сообщении




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


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


1280   15  

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.Abs on 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))

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

(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)

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

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

    Ничего не найдено.