2015-07-22 6 views
4

Я реализую простой фильтр частиц с двумя состояниями. Если вы не знаете, что такое фильтр частиц, это прекрасно - короткая версия заключается в том, что мне нужно вычислить взвешенные средства с весами между 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.

+0

Как вы подтверждаете, что значение действительно 1,0? Код 'double value = 1.0; if (значение> 1.01) {Console.WriteLine ("wtf"); } 'ведет себя как ожидалось (не печатает wtf). –

+2

Можете ли вы попробовать 'Console.WriteLine (« Value »+ p.Value.ToString (« 0.0000000 »));' –

+0

FWIW, ваш код выглядит правильно для меня. Однако только для написания более четкого кода я бы переименовал 'double mean' в' double weightedTotal' и изменил 'mean/= totalWeight;' на 'double mean = weightedTotal/totalWeight'. –

ответ

2

Ладно, ребята собираются быть с ума, так что позвольте мне начать с того, что я извиняюсь :-)

Проблема заключалась в том, состояние гонки, и было связано с непониманием на мой часть о том, как работают блокировки на C#. Я блокировал объект, чей экземпляр мог меняться разными способами, в которых мешок частиц менялся. Замена этого с помощью выделенного объекта блокировки устранила мои проблемы.

Извините^_^;;

+0

LOL Я действительно думал об этом как о ответе, но ничего не сказал. Как только я увидел ConcurentBag, сигнальные колокола погасли. Я должен был что-то сказать. Хорошая работа по поиску решения и опубликовать это как ответ на свой вопрос. – thinklarge