2008-09-27 13 views
5

Postanowiłem więc spróbować rozwiązać zadanie domowe z fizyki, pisząc kilka skryptów python, aby rozwiązać problemy dla mnie. Jednym z problemów, na które napotykam, jest to, że znaczące liczby nie zawsze wydają się poprawne. Na przykład ten obsługuje cyfry znaczące poprawnie:Znaczące liczby w module dziesiętnym

from decimal import Decimal 
>>> Decimal('1.0') + Decimal('2.0') 
Decimal("3.0") 

Ale tego nie robi:

>>> Decimal('1.00')/Decimal('3.00') 
Decimal("0.3333333333333333333333333333") 

Więc dwa pytania:

  1. Mam rację, że nie jest to oczekiwana kwota znaczące cyfry, czy muszę odświeżyć znaczącą cyfrową matematykę?
  2. Czy można to zrobić bez konieczności ręcznego ustawiania dokładności dziesiętnej? Oczywiście, jestem pewien, że mogę użyć numpy do zrobienia tego, ale chcę tylko wiedzieć, czy jest jakiś sposób na zrobienie tego z modułem dziesiętnym z ciekawości.

Odpowiedz

8

Zmiana dokładności dziesiętnej na 2 cyfry to dobry pomysł, chyba że musisz wykonać tylko jedną operację.

Powinieneś zawsze wykonywać obliczenia z większą dokładnością niż poziom istotności, a tylko wokół końcowego wyniku. Jeśli wykonasz długą sekwencję obliczeń i zaokrąglisz liczbę cyfr znaczących na każdym kroku, błędy będą się kumulować. Moduł dziesiętny nie wie, czy dana operacja jest długa, czy końcowy, więc zakłada, że ​​nie powinna zaokrąglić więcej niż to konieczne. Idealnie byłoby używać nieskończonej precyzji, ale to jest zbyt kosztowne, więc programiści Pythona ustalili 28 cyfr.

Po przybył do wyniku końcowego, co prawdopodobnie chcesz to kwantyzacji:

 
>>> (Decimal('1.00')/Decimal('3.00')).quantize(Decimal("0.001")) 
Decimal("0.333") 

Trzeba śledzić znaczenia ręcznie. Jeśli chcesz automatyczne śledzenie istotności, powinieneś użyć arytmetyki interwałowej. Istnieje kilka bibliotek dostępnych dla Pythona, w tym pyinterval i mpmath (które obsługują dowolną precyzję). Łatwo jest również zaimplementować arytmetykę przedziałową z biblioteką dziesiętną, ponieważ obsługuje ona zaokrąglanie bezpośrednie.

Można też przeczytać Decimal Arithmetic FAQ: Is the decimal arithmetic ‘significance’ arithmetic?

+0

Co z 300/100? Twój kod zostałby nieprawidłowo ustawiony na 3.000 – Pyrolistical

0

Dziesiętna wartość domyślna to 28 miejsc precyzji.
Jedynym sposobem ograniczenia liczby cyfr, które zwraca, jest zmiana dokładności.

+0

To niekoniecznie zawsze jest prawdą. Na przykład: >>> Dziesiętny ("1.0") * Dziesiętny ("1,0") daje dziesiętny ("1,00"). Może to właśnie mówisz w kontekście podziału? –

2

Dziesiątki nie będą wyrzucać takich miejsc dziesiętnych. Jeśli naprawdę chcesz ograniczyć precyzję do 2 d.p. spróbuj

decimal.getcontext().prec=2 

EDIT: Można alternatywnie nazwać kwantyzacji() za każdym razem należy pomnożyć lub podzielić (dodawanie i odejmowanie będzie zachować 2 dps).

+0

Hmmm ... więc nie ma sposobu, aby to zrobić bez wymogu ręcznego ustawiania precyzji? –

0

Jeśli podaję wartość dziesiętną poprawnie, "precyzja" to liczba cyfr po przecinku w zapis dziesiętny.

Wydaje się, że chcesz czegoś innego: liczby cyfr znaczących. Jest to o jeden więcej niż liczba cyfr po kropce dziesiętnej w notacji naukowej.

Byłbym zainteresowany poznawaniem modułu Pythona, który wykonuje obliczenia punktowe zmiennoprzecinkowe z istotnymi cyframi.

0

Co jest nie tak z zmiennoprzecinkowym?

>>> "%8.2e"% (1.0/3.0) 
'3.33e-01' 

Został zaprojektowany do obliczeń w stylu naukowym z ograniczoną liczbą cyfr znaczących.

+0

Problem polega na tym, że nadal ustawiałbym ręcznie liczbę cyfr znaczących. –

+0

Na pewno nie widzę problemu z ustawieniem znaczących cyfr. Będziesz musiał rozwinąć swoje pytanie, aby pokazać, co chcesz i dlaczego nie możesz/nie chcesz ustawić znaczących cyfr. –

1

Tak z ciekawości ... jest to niezbędne do korzystania z modułu dziesiętną? Czemu nie zmiennoprzecinkowe z liczbami zaokrąglonymi liczbowo, kiedy jesteś gotowy, aby je zobaczyć? Lub czy próbujesz śledzić znaczące liczby obliczeń (np. Kiedy musisz wykonać analizę błędu wyniku, obliczając błąd obliczeniowy jako funkcję niepewności, które przeszły do ​​obliczeń)? Jeśli chcesz funkcję zaokrąglania, że ​​pocisków z lewej strony liczby, zamiast w prawo, spróbuj:

def lround(x,leadingDigits=0): 
    """Return x either as 'print' would show it (the default) 
    or rounded to the specified digit as counted from the leftmost 
    non-zero digit of the number, e.g. lround(0.00326,2) --> 0.0033 
    """ 
    assert leadingDigits>=0 
    if leadingDigits==0: 
      return float(str(x)) #just give it back like 'print' would give it 
    return float('%.*e' % (int(leadingDigits),x)) #give it back as rounded by the %e format 

Numery będą wyglądać dobrze, kiedy je wydrukować lub przekonwertować je na ciągi znaków, ale jeśli pracujesz u pytaj i nie jawnie je drukuj, mogą wyglądać nieco dziwnie:

>>> lround(1./3.,2),str(lround(1./3.,2)),str(lround(1./3.,4)) 
(0.33000000000000002, '0.33', '0.3333') 
Powiązane problemy