Strona główna Polish Python Coders Group
   Strona główna   Pomoc Zaloguj się Rejestracja  
Witamy, Gość. Zaloguj się lub zarejestruj.
Czy dotarł do Ciebie email aktywacyjny?

Zaloguj się podając nazwę użytkownika, hasło i długość sesji

Aktualności: ##python.pl na FreeNode - ogarniamy zlot na Mazurach lub Podlasiu :>?
Szukaj Szukaj
Strony: [1]   Do dołu
Drukuj
Wątek: Zaokrąglanie liczb  (Przeczytany 7548 razy)
« : 21:46 27/12/08 »
Katharsis Offline
Administrator
Professional Python User

Zobacz profil WWW
*****

Reputacja: 63
Płeć: Mężczyzna
Wiadomości: 494


Cześć.

Ostatnio natknąłem się na problem dot. zaokrąglania liczb zmiennoprzecinkowych w Pythonie. Potrzebuję zrobić kilka ważnych obliczeń, które mają dalszy wpływ na zachowanie programu, a bardzo zależy mi na dokładności wyniku. O jakiej dokładności mówię.

Przykład: Liczenie mantysy (IEEE (32bit) to Decimal):

Kod
ujemne = range(-40,0)
ujemne.reverse()
 
liczba = float("0.412")
lista = []
 
for x in ujemne:
 
   sprawdzaczka = 2**x
 
   if sprawdzaczka <= liczba:
       liczba = liczba - sprawdzaczka
       lista.append('1')
 
   else:
       lista.append('0')
 
lista = ''.join(lista)
mantysa = lista[2:25]
 
print mantysa

Zakładam, że znane jest Wam pojęce mantysy. W moim przypadku ten kod zwraca:

Cytuj
10100101111000110101001

Co jest równoznaczne z liczbą:

Cytuj
0.41199997

Cały problem w tym, że Python liczbę 0.412 traktuję jako 0.41199999999999998. Jeżeli uda się ujarzmić ten problem, prawidłowym wynikiem jest:

Cytuj
10100101111000110101010

Próbowałem z Decimal i FixedPoint, wg porad kolegów z kanału, jednak nadal stoję w miejscu. jak widać nie zgadzają się tylko 2 ostatnie bity mantysy.

Czekam na Wasze pomysły. Z góry dzięki.
Zapisane

PyStok - Białostocka Grupa Użytkowników Pythona - pystok.org
« Odpowiedz #1 : 23:32 27/12/08 »
pigmej Offline
PPCG
Professional Python User

Zobacz profil
*****

Reputacja: 50
Płeć: Mężczyzna
Wiadomości: 343


Większy problem:

Kod
In [1]: 0.412==0.41199999999999998
Out[1]: True
 
In [2]: 0.412==0.41199999999999999
Out[2]: True
 
In [3]: 0.412==0.41199999999999997
Out[3]: True
 
In [4]: 0.412==0.41199999999999993
Out[4]: False
 
In [5]: 0.412==0.41199999999999994
Out[5]: False
 
In [6]: 0.412==0.41199999999999995
Out[6]: True

Może numpy pomoże?
Zapisane
« Odpowiedz #2 : 18:23 05/01/09 »
zuo Offline
Professional Python User

Zobacz profil
***

Reputacja: 129
Wiadomości: 425


Spróbowałem użyć ułamków zwykłych, co powinno gwarantować dokładność – i...:

Kod
# (python 2.6 lub 3.0)
 
from __future__ import print_function
from fractions import Fraction
 
liczba = "0.412"
zakres = 25
print("liczba:", liczba)
print("zakres:", zakres)
 
liczba = Fraction(liczba)
lista = []
for x in reversed(range(-zakres, 0)):
   spr = Fraction(2) ** x
   if spr <= liczba:
       print(spr, '<=', liczba)
       liczba -= spr
       lista.append('1')
   else:
       print(spr, '>', liczba)
       lista.append('0')
 
mantysa = ''.join(lista)[2:zakres]
print("mantysa:", mantysa)

Wyświetla:

Kod:
liczba: 0.412
zakres: 25
1/2 > 103/250
1/4 <= 103/250
1/8 <= 81/500
1/16 > 37/1000
1/32 <= 37/1000
1/64 > 23/4000
1/128 > 23/4000
1/256 <= 23/4000
1/512 > 59/32000
1/1024 <= 59/32000
1/2048 <= 111/128000
1/4096 <= 97/256000
1/8192 <= 69/512000
1/16384 > 13/1024000
1/32768 > 13/1024000
1/65536 > 13/1024000
1/131072 <= 13/1024000
1/262144 <= 83/16384000
1/524288 > 41/32768000
1/1048576 <= 41/32768000
1/2097152 > 39/131072000
1/4194304 <= 39/131072000
1/8388608 > 31/524288000
1/16777216 > 31/524288000
1/33554432 <= 31/524288000
mantysa: 10100101111000110101001

Czy jesteś pewien, że 10100101111000110101001 to zły wynik i że powinno był... 1010?

Pozdrawiam,
Zapisane
« Odpowiedz #3 : 22:01 05/01/09 »
zuo Offline
Professional Python User

Zobacz profil
***

Reputacja: 129
Wiadomości: 425


Wiersze 13 i 14 można trochę skrócić:

Kod
for x in range(1, zakres + 1):
   spr = Fraction(1, 2 ** x)

Wynik jest ten sam.
Zapisane
« Odpowiedz #4 : 14:50 06/01/09 »
Katharsis Offline
Administrator
Professional Python User

Zobacz profil WWW
*****

Reputacja: 63
Płeć: Mężczyzna
Wiadomości: 494


Niestety jestem pewien. IEEE 754 Converter to aplikacja w Javie, która liczy prawidłowo całą reprezentacje. Poeksperymentuj z Decimal2IEEE, IEEE2Decimal, czy to na 0.412 czy 0.41199999999999998, wnioski nasuną się same.

Mimo wszystko, dziękuję za ponowne zainteresowanie problemem. Nadal z nim walczę Uśmiech.
Zapisane

PyStok - Białostocka Grupa Użytkowników Pythona - pystok.org
« Odpowiedz #5 : 21:06 06/01/09 »
zuo Offline
Professional Python User

Zobacz profil
***

Reputacja: 129
Wiadomości: 425


Nie znam się specjalnie na liczbach zmiennoprzecinkowych, ale:

Sami autorzy tego javowego appletu piszą:
  • "The applet is limited to single precision numbers (32 Bit) for space reasons.”
  • "To make it easier to spot eventual rounding errors, the selected float number is displayed after conversion to double precision.”
  • "Rounding errors: Not every decimal number can be expressed exactly as a floating point number. This can be seen when entering "0.1" and examining its binary representation which is either slightly smaller or larger, depending on the last bit.”

A więc ten applet używa single float (32 bity), a jedynie dodatkowo pokazuje wynik konwersji podanej liczby na double (właśnie po to, by ułatwić oszacowanie możliwego błędu).

Podczas, gdy Python wewnętrznie używa double (zwykle 64 bity).

Liczbę 0,412:
– ten applet wyświetla jako 0.41200000047683716 (po konwersji na "with double precision")
– Python wyświetla jako 0.41199999999999998 (co jest znacznie lepszym przybliżeniem)

Jeżeli zmienisz w pythonowym kodzie zadaną liczbę na owo bardzo zgrubne przybliżenie 0.41200000047683716, to uzyskasz właśnie 10100101111000110101010 – natomiast zarówno dla 0.412 (dla Pythona jest to 0.41199999999999998), jak i dla 0.41200000000000001 (dla Pythona jest to 0.41200000000000003) wychodzi 10100101111000110101001.

Ergo: to Python ma rację, nie ten javowy applet.

(Pomijam już to, co wynika z prześledzenia wyżej wylistowanych operacji na ułamkach zwykłych, które zresztą w ogóle nie powinny wprowadzać jakichkolwiek zakłamań i jak sądzę nie wprowadzają).

@Edit:
s/aplet/applet/g Uśmiech.
Zapisane
« Odpowiedz #6 : 15:20 10/01/09 »
zuo Offline
Professional Python User

Zobacz profil
***

Reputacja: 129
Wiadomości: 425


PS. Oczywiście, jeżeli zależy Ci nie na maksymalnej dokładności, a na konkretnej ustandaryzowanej niedokładności wynikającej z 32-bitowej zmiennoprzecinkowej reprezentacji liczb rzeczywistych czyli single float – to Python Ci tego nie daje (musiałbyś to w nim ręcznie zaimplementować), ale właśnie dlatego, że w operacjach na liczbach zmiennoprzeninkowych jest bardziej dokładny (używa double float, co na ogół oznacza 64-bitową reprezentację).
Zapisane
« Odpowiedz #7 : 01:36 13/01/09 »
Katharsis Offline
Administrator
Professional Python User

Zobacz profil WWW
*****

Reputacja: 63
Płeć: Mężczyzna
Wiadomości: 494


W porządku! Dzięki za udział w temacie. W wolnej chwili dokończę aplikację Uśmiech.
Zapisane

PyStok - Białostocka Grupa Użytkowników Pythona - pystok.org
« Odpowiedz #8 : 14:24 19/04/09 »
SeC Offline
Hello World!

Zobacz profil
*

Reputacja: 6
Wiadomości: 58


Jeśli chodzi o porównywanie floatow, ja polecam coś takiego:

Kod
In [46]: f1
Out[46]: 0.12119999999999999
 
In [47]: f2
Out[47]: 0.1212
 
In [48]: f1 == f2
Out[48]: False
 
In [49]: def fcmp(f1, f2):
  ....:     d = f1-f2
  ....:     if abs(d) < 1.0e-8:
  ....:         return 0
  ....:     else:
  ....:         return -1 if d<0 else 1
  ....:
  ....:
 
In [50]: fcmp(f1, f2)
Out[50]: 0
Zapisane

Strony: [1]   Do góry
Drukuj
Skocz do:  

© 2007 - 2018 Polish Python Coders Group
Powered by SMF 1.1.21 | SMF © 2006-2009, Simple Machines | Theme by PixelSlot