Содержится в списке двойников

Я использую Visual Studio Immediate Window для проверки содержимого моего списка. Вот что я вижу:

found_numbers
Count = 36
    [0]: 50,82
    [1]: 3358
    [2]: 954
    [3]: 5571
    [4]: 3142
    [5]: 700
    [6]: 322
    [7]: 402
    [8]: 1231
    [9]: 4118
    [10]: 4532
    [11]: 0
    [12]: 0
    [13]: 3101
    [14]: 18
    [15]: 0
    [16]: 0
    [17]: 8896
    [18]: 0
    [19]: 4,01
    [20]: 19,5
    [21]: 0,78
    [22]: 20,28
    [23]: 10
    [24]: 27,76
    [25]: 2,78
    [26]: 30,54
    [27]: 4648
    [28]: 508
    [29]: 1,51
    [30]: 4648
    [31]: 508
    [32]: 1,51
    [33]: 0,28
    [34]: 0,28
    [35]: 0,56

Затем я проверяю значение двух моих переменных

total1
50,82
total3
20,28

На последнем шаге я хочу увидеть, содержит ли мой список эти значения, и это мой вывод:

found_numbers.Contains(total1)
false
found_numbers.Contains(total3)
true

Я действительно смущен этим поведением, поскольку оба числа находятся в данном списке. Почему это могло происходить? Редактировать: оба всего1 и всего3 имеют двойной тип.

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

TaW 04.04.2022 14:37

Поскольку это значения double, возможно, что два значения «50,82» не совсем совпадают, особенно если одно из них было рассчитано из-за неточности чисел с плавающей запятой. Обычно вам нужно проверить, находятся ли числа с плавающей запятой в пределах дельты друг от друга.

juharr 04.04.2022 14:37

Проверьте значения точныйdouble: found_numbers[0].ToString("R") и total1.ToString("R"); вы можете обнаружить, что некоторые из значений 50,82 на самом деле являются 50,820000000001 или 50.819999999999998, и поэтому у вас есть found_numbers.Contains(total1) == false

Dmitry Bychenko 04.04.2022 14:39

Вы назначаете переменную как total1 = 50.82 или total1 = found_numbers[0] ? Первый, скорее всего, не даст точно такое же число, как в списке.

JonasH 04.04.2022 14:39

Если это суммы в валюте, то я согласен с @TaW, что вместо этого вы должны использовать decimal.

juharr 04.04.2022 14:41

50,82 — результат операции округления. Я проверил позже, и, как вы предположили, числа не были полностью равными. Изменение его на десятичное сделало свое дело

alexgg94 04.04.2022 14:55

Вы используете .NET Framework или .NET? В первом случае есть четырнадцать различных значений double, которые могут соответствовать строке "50,82", наименьшее из них — BitConverter.Int64BitsToDouble(4632349096467717154L), а наибольшее — BitConverter.Int64BitsToDouble(4632349096467717167L). Разница в .NET заключается в том, что по умолчанию отображается много десятичных знаков, так что вы видите разницу, и только говоря .ToString("G15") или подобное, вы можете получить предыдущее поведение.

Jeppe Stig Nielsen 04.04.2022 15:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Когда мы делаем вычисления с double, мы получаем ошибки округление, и поэтому вместо 50,82 у нас фактически есть 50,82000000001 или 50,81999999998. Все эти значения обычно представлены, являющиеся округлый как 50,82, и у нас есть странные found_numbers.Contains(total1) == false. Чтобы получить значение точныйdouble, используйте формат R;

found_numbers[0].ToString("R")
total1.ToString("R")

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

  1. Переключитесь с double на decimal (особенно если значения имеют десятичную точку исправлено, например, если значения являются валютами)
  2. Используйте толерантность при сравнении:
//TODO: put the right value here
double tolerance = 1e-8;

bool found = found_numbers.Any(item => Math.Abs(item - total1) <= tolerance);

Обратите внимание, что некоторый зависит от версии .NET. В .NET Framework (например, .NET Framework 4.8 и более ранних версиях) что-то вроде (25.0 / 3.0).ToString() будет производить "8,33333333333333", скрывая часть точности (запятая зависит от текущего языка и региональных параметров). Однако в .NET Core, начиная с .NET Core 3.0, и, следовательно, также в .NET 5, .NET 6 и т. д., одно и то же выражение (25.0 / 3.0).ToString() дает более длинную строку "8,333333333333334", поэтому вам не нужно передавать строку формата "R" в тот случай. Я предполагаю, что спрашивающий находится на .NET Framework.

Jeppe Stig Nielsen 04.04.2022 15:02

Другие вопросы по теме