Я использую 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 имеют двойной тип.
Поскольку это значения double, возможно, что два значения «50,82» не совсем совпадают, особенно если одно из них было рассчитано из-за неточности чисел с плавающей запятой. Обычно вам нужно проверить, находятся ли числа с плавающей запятой в пределах дельты друг от друга.
Проверьте значения точныйdouble: found_numbers[0].ToString("R") и total1.ToString("R"); вы можете обнаружить, что некоторые из значений 50,82 на самом деле являются 50,820000000001 или 50.819999999999998, и поэтому у вас есть found_numbers.Contains(total1) == false
Вы назначаете переменную как total1 = 50.82 или total1 = found_numbers[0] ? Первый, скорее всего, не даст точно такое же число, как в списке.
Если это суммы в валюте, то я согласен с @TaW, что вместо этого вы должны использовать decimal.
50,82 — результат операции округления. Я проверил позже, и, как вы предположили, числа не были полностью равными. Изменение его на десятичное сделало свое дело
Вы используете .NET Framework или .NET? В первом случае есть четырнадцать различных значений double, которые могут соответствовать строке "50,82", наименьшее из них — BitConverter.Int64BitsToDouble(4632349096467717154L), а наибольшее — BitConverter.Int64BitsToDouble(4632349096467717167L). Разница в .NET заключается в том, что по умолчанию отображается много десятичных знаков, так что вы видите разницу, и только говоря .ToString("G15") или подобное, вы можете получить предыдущее поведение.





Когда мы делаем вычисления с double, мы получаем ошибки округление, и поэтому вместо 50,82 у нас фактически есть 50,82000000001 или 50,81999999998. Все эти значения обычно представлены, являющиеся округлый как 50,82, и у нас есть странные found_numbers.Contains(total1) == false. Чтобы получить значение точныйdouble, используйте формат R;
found_numbers[0].ToString("R")
total1.ToString("R")
попробуйте эти представления, и вы увидите разницу. Если вы хотите избавиться от проблем с округлением, вы можете:
double на decimal (особенно если значения имеют десятичную точку исправлено, например, если значения являются валютами)//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.
Двойники сложно сравнивать из-за проблем с плавающей запятой. Можете ли вы вместо этого использовать десятичные дроби?