Duże liczby w Pythonie
Python jest językiem wyjątkowym. Jednym z reprezentantów tej wyjątkowości jest sposób przechowywania dużych liczb.
Liczby zmiennopozycyjne Python przechowuje jak każdy inny język - cecha i mantysa. Dla liczb liczb całkowitych stosowany jest całkowicie inny sposób przechowywania. Jeżli liczba mieści się w zakresie sys.maxint
(czyli 2**63-1 lub 2**31-1 w zależności od bitowości instalacji Pythona) to wszystko jest jak w “normalnych” środowiskach C-podobnych. Jednak po czymś w rodzaju “inta” nie ma “longa” tylko bardziej “BigInteger”.
Kiedy wykorzystujemy środowisko do obliczeń wypada poznać zakres i dokładność dostępnych struktu liczbowych.
Najprostszy pomiar dokładności polega na sprawdzaniu kolejnych potęg 10 na wrażliwość na dodanie jakiejś małej wartości, która nas interesuje - będzie to miara dokładności - dla zmiennoprzecinkowych weźmiemy celem przykładu 0.1, a całkowitych najmniejszą możliwą - 1. (Można zapytać po co sprawdzać dokładność inta - chcemy zapobiec typowego dla środowisk skryptowych cichego zrzutowania na typ inny niż integer np. double)
W pętli wystarczy teraz mnożyć liczbę razy 10 i porównać wartość zmiennej liczba i (liczba+encjaTestowa). Przykładowy kod w języku, który rozważamy wygląda następująco:
def czyDokladneFloat(liczba):
liczba1 = float(liczba)
liczba2 = float(liczba+0.1)
return ( float(liczba1) < float(liczba2) )
def czyDokladneInt(liczba):
liczba1 = int(liczba)
liczba2 = int(liczba+1)
return ( int(liczba1) < int(liczba2) )
licznik = 1
dokladna = True
print("Sprawdzam zmiennoprzecinkowe:")
while dokladna:
liczba = 10**licznik
dokladna = czyDokladneFloat(liczba)
print ("10^"+str(licznik)+": "+("NIE" if not dokladna)+"dokładna")
licznik+=1
licznik = 1
dokladna = True
print("Sprawdzam stałopozycyjne:")
while dokladna:
liczba = 10**licznik
dokladna = czyDokladneInt(liczba)
print ("10^"+str(licznik)+": "+("NIE" if not dokladna)+"dokładna")
licznik+=100 #+=100 bo inaczej idzie strasznie powoli
Castowanie w funkcjach wymusza użycie tego konkretnie typu bez “sprytnych” konwersji i ewentualnych operacji na łańcuchach znaków.
Co się okazuje? Maksymalna wartość float’a dokładnego do 0.1 to 10^16 (podobnie jak w typowych językach kompilowanych i skryptowych), a dla 1 w integerach… jest nieskończona. To znaczy jak poczekamy odpowiednio długo skończy nam się pamięć.
Oczywiście w językach/środowiskach, które zapewniają rozróżnienie na liczby ze znakiem i w wyniku przepelnienia “przekręcają się” dając dla MAX+1 wartość MIN należy implementując powyższe testy pamiętać o ograniczeniu pętli do liczb dodatnich.
W ogólności dokładność Integera w Pythonie 3 to stała = 1. Tak też jest w językach niższego poziomu. Jednak wysokopoziomowe środowiska mogą stosować rzutowanie na różne dziwne struktury i niekoniecznie rozwróżniać gogol od (gogol+1).
Wiedza o obsłudze ogromnych (>10**30) liczb calkowitych pozwala np. generować dane do stablicowania. W Pythonie można się nawet pokusić o osłonienie liczb zmiennoprzecinkowych jako klasy składającej się z Int liczba i Int dokładność - potęga dziesiątki. Umożliwia to prawie nieskończoną dokładność tak cenną w wielu projektach pomocniczych przy jednocześnie niskim koszcie implementacji (zamiast tworzenia własnych struktur w C++ wystarczy jeden skrypt w Pythonie).