Я реализую простой фильтр частиц с двумя состояниями. Если вы не знаете, что такое фильтр частиц, это прекрасно - короткая версия заключается в том, что мне нужно вычислить взвешенные средства с весами между 0 и 1 и значениями между 0 и 1. Каждая частица имеет значение и вес.Необычные числовые ошибки
C# дает мне абсолютно причудливые числовые проблемы.
В попытке отладки это, это то, что мой код выглядит следующим образом:
ConcurrentBag<Particle> particles; //this is inputted as an argument to my function
double mean = 0.0;
double totalWeight = 0.0;
foreach (Particle p in particles)
{
mean += p.Value * p.Weight;
totalWeight += p.Weight;
if (p.Value > 1.01 || p.Weight > 1.01)
{
Console.WriteLine("Value " + p.Value);
Console.WriteLine("Weight " + p.Weight);
Console.WriteLine("wtf");
}
}
if (totalWeight == 0.0)
{
//in this case, everything has miniscule weight, so let's just return 0.0 to avoid this precision corner case.
return new Bernoulli(0.0);
}
double oldMean = mean;
mean /= totalWeight;
return mean;
что если оператор с «WTF» есть для отладки, и это время срабатывает. Но печать при отъезде:
Значение 1,0 Вес 0,01
Это если заявление не должно быть правдой на всех! Что происходит?
Редактировать: Небольшое обновление при отладке. Это моя текущая целая функция:
public override IDistribution createDistribution(ConcurrentBag<Particle> particles)
{
if (particles.Count == 0)
{
throw new Exception("Cannot create Distribution from empty particle collection");
}
if (!particles.ToArray()[0].GetType().IsAssignableFrom(typeof(BinaryParticle)))
{
throw new Exception("Cannot create Bernoulli Distribution from non-Binary Particle");
}
decimal mean = 0.0m;
decimal totalWeight = 0.0m;
foreach (Particle p in particles)
{
mean += (decimal)(p.Value * p.Weight);
totalWeight += (decimal)p.Weight;
if ((p.Weight > 1.01))
{
{
Console.WriteLine("Value " + p.Value);
Console.WriteLine("Weight " + p.Weight);
Console.WriteLine("Value " + p.Value.ToString("0.0000000"));
Console.WriteLine("wtf");
}
}
if (totalWeight == 0.0m)
{
//in this case, everything has miniscule weight, so let's just return 0.0 to avoid this precision corner case.
return new Bernoulli(0.0);
}
decimal oldMean = mean;
mean /= totalWeight;
try
{
return new Bernoulli((double)mean);
}
catch (Exception e)
{
decimal testMean = 0.0m;
decimal testTotalWeight = 0.0m;
Console.WriteLine(e);
foreach (Particle p in particles)
{
testMean += (decimal)(p.Value * p.Weight);
testTotalWeight += (decimal)p.Weight;
Console.WriteLine("weight is " + p.Weight);
Console.WriteLine("value is " + p.Value);
Console.WriteLine("Total mean is " + testMean);
Console.WriteLine("Total weight is " + testTotalWeight);
}
Console.WriteLine(testMean/testTotalWeight);
throw new Exception();
}
}
«mean» дает другое значение, чем печатается в записи в блоке catch. Понятия не имею почему. Кроме того, странно, это вес> 1,01, что является истинным условием, когда вес составляет 0,01.
Как вы подтверждаете, что значение действительно 1,0? Код 'double value = 1.0; if (значение> 1.01) {Console.WriteLine ("wtf"); } 'ведет себя как ожидалось (не печатает wtf). –
Можете ли вы попробовать 'Console.WriteLine (« Value »+ p.Value.ToString (« 0.0000000 »));' –
FWIW, ваш код выглядит правильно для меня. Однако только для написания более четкого кода я бы переименовал 'double mean' в' double weightedTotal' и изменил 'mean/= totalWeight;' на 'double mean = weightedTotal/totalWeight'. –