2012-05-28 14 views
5

Dzień dobry wszystkim,Ruby matematyki zmiennoprzecinkowej - Problem z precyzja Suma Calc

Mam pewne problemy z matematyką liczb zmiennoprzecinkowych i zdobyć całkowicie zagubiony w „.to_f” 's «* 100» 's i ".0"!

Miałem nadzieję, że ktoś może mi pomóc z moim konkretnym problemem, a także wyjaśnić dokładnie, dlaczego ich rozwiązanie działa, tak, że rozumiem to na następny raz.

Mój program musi zrobić dwie rzeczy:

  1. Sum listę miejsc po przecinku, określenia, czy sumują się dokładnie 1,0
  2. Określić różnicę między 1,0 a sumę liczb - ustawić wartość zmienna na dokładną różnicę, aby suma wynosiła 1,0.

Na przykład:

  1. [0,28, 0,55, 0,17] -> powinny sumować się do 1,0, jednak wciąż otrzymuję 1.xxxxxx. Jestem wykonawczych sumę w następujący sposób:

    sum = array.inject(0.0){|sum,x| sum+ (x*100)}/100 
    
  2. Powodem potrzebuję tej funkcji jest to, że czytam w zestawie dziesiętnych, które pochodzą z programu Excel. Nie są one w 100% dokładne (brakuje im dziesiętnych punktów), więc suma zwykle wynosi 0,999999xxxxx lub 1.000xxxxx. Na przykład, będę się wartości jak poniżej:

    0.568887955,0.070564759,0.360547286 
    

Aby rozwiązać ten problem, jestem w porządku biorąc sumę pierwszych n-1 liczb, a następnie zmianę ostatecznej liczby lekko tak, że wszyscy liczby razem sumują się do 1,0 (muszą spełniać walidację za pomocą powyższego równania lub cokolwiek kończę). Jestem obecnie wdrożenie to w następujący sposób:

  sum = 0.0 
      array.each do |item| 
      sum += item * 100.0 
      end 
      array[i] = (100 - sum.round)/100.0 

wiem, co mogłem zrobić to z inject, ale starałem się grać z nim, aby zobaczyć, co działa. Myślę, że to na ogół działa (od sprawdzania wyników), ale nie zawsze spełnia powyższą sumę walidacji. Więc jeśli zajdzie taka potrzeba, mogę też to zmienić. Zauważ, że potrzebuję tylko dwóch dziesiętnych dokładności w tych liczbach - 0,56, a nie 0,5623225. Mogę je zaokrąglić w czasie prezentacji lub podczas obliczeń ... Nie ma to dla mnie znaczenia.

Dziękuję BARDZO ZA POMOC!

Odpowiedz

8

Jeśli dokładność jest dla Ciebie ważna, nie powinieneś używać wartości zmiennoprzecinkowych, które z definicji nie są dokładne.Ruby ma pewne precyzyjne typy danych do robienia arytmetyki, gdzie ważna jest dokładność. Są one z góry mojej głowy, BigDecimal, Rational i Complex, w zależności od tego, co faktycznie trzeba obliczyć.

Wygląda na to, że w twoim przypadku to, czego szukasz, to BigDecimal, czyli w zasadzie liczba ze stałą liczbą cyfr, z których po kropce dziesiętnej jest stała liczba cyfr (w przeciwieństwie do zmiennoprzecinkowy, który ma dowolną liczbę cyfr po kropce dziesiętnej).

Podczas czytania z Excela i celowego rzucania tych ciągów jak "0.9987" na zmiennoprzecinkowe, natychmiast tracisz dokładną wartość zawartą w łańcuchu.

require "bigdecimal" 
BigDecimal("0.9987") 

Ta wartość jest precyzyjna. To 0,9987. Nie 0.9, ani nic podobnego, ale 0,9987. Możesz użyć wszystkich zwykłych operacji arytmetycznych na nim. Pod warunkiem, że nie mieszasz punktów zmiennoprzecinkowych z operacjami arytmetycznymi, wartości zwracane pozostaną dokładne.

Jeśli tablica zawiera surowe ciągi masz z Excela (to znaczy nie #to_f byłyby im), wtedy to daje BigDecimal, że jest różnica między sumą nich i 1.

1 - array.map{|v| BigDecimal(v)}.reduce(:+) 
+0

Dzięki! Pomocna odpowiedź. – Brandon

2

Albo:

  • nadal używać pływaków i round(2) swoje Łącznie: 12.341.round(2) # => 12.34

  • użyć liczb całkowitych (tzn centów zamiast dolarów)

  • stosowanie BigDecimal i nie trzeba będzie do po podsumowaniu, tak długo, jak zaczynasz od BigDecimal z tylko dwoma miejscami dziesiętnymi.

0

Myślę, że algorytmy mają o wiele więcej wspólnego z dokładnością i precyzją niż wybór zmiennoprzecinkowego punktu IEEE w stosunku do innej reprezentacji.

Ludzie dokonywali drobnych obliczeń, wciąż mając do czynienia z zagadnieniami dokładności i precyzji. Zrobiliby to, zarządzając algorytmami, których użyliby i rozumiejąc, jak reprezentować funkcje głębiej. Myślę, że możesz popełnić błąd, odrzucając to lepsze zrozumienie i zakładając, że innym rozwiązaniem jest rozwiązanie.

Na przykład, wielomianowa reprezentacja funkcji nie będzie poprawnie traktować asymptoty lub osobliwości.

Nie należy odrzucać zmiennoprzecinkowego tak szybko. Mogę być mądrzejszy w sposobie, w jaki ich użyjesz.