Максимално искористити бројеве са децималима

Jedan od najčešćih tipova podataka u programskom jeziku Python jeste *float*, koji služi za reprezentovanje brojeva sa pokretnim zarezom. Ovi brojevi, bilo pozitivni ili negativni, karakterišu se postojanjem decimalnog dela. Takođe, u ovu kategoriju spadaju i brojevi prikazani u naučnoj notaciji, gde se simboli ‘e’ ili ‘E’ koriste za označavanje stepena broja 10.

Tip *float* je izuzetno važan jer omogućava prikaz širokog spektra realnih brojeva, od izuzetno malih do veoma velikih vrednosti.

U nastavku su prikazani primeri upotrebe brojeva sa pokretnim zarezom u Python-u:

# float numbers
a = 20.0
b = -51.51345
c = 65e7
d = -1.08E12
e = 2E10

print(type(a))
print(type(b))
print(type(c))
print(type(d))
print(type(e))

Izlaz:

<class 'float'>
<class 'float'>
<class 'float'>
<class 'float'>
<class 'float'>

Pored toga, ovaj tip podataka omogućava preciznije računske operacije u poređenju sa tipovima poput celih brojeva, kod kojih se decimalni deo odbacuje. Na primer, broj 3.142 bi kod celih brojeva bio predstavljen kao 3, dok bi *float* tip prikazao taj broj u njegovom izvornom obliku, sa decimalnim delom. Shodno tome, *float* vrednosti su pogodnije za matematičke proračune jer daju tačnije rezultate.

U skladu s tim, vrednosti sa pokretnim zarezom se široko primenjuju u modeliranju realnih situacija, mašinskom učenju, obradi podataka, finansijskoj i ekonomskoj analizi, matematičkim proračunima, grafičkom prikazu i vizualizaciji, kao i u naučnim i inženjerskim proračunima.

Celi brojevi nasuprot *float* tipu u Python-u

Celi brojevi predstavljaju još jedan veoma korišćen tip podataka u Python-u. Za razliku od brojeva sa pokretnim zarezom, celi brojevi ne sadrže decimalni zarez. Celi brojevi obuhvataju pozitivne cele brojeve, negativne cele brojeve i nulu, bez ikakvog decimalnog dela.

Celi brojevi su veoma korisni kada obavljamo operacije koje uključuju cele brojeve, poput brojanja ili indeksiranja. U Python-u, celobrojne vrednosti su označene kao *int*.

Neki primeri celih brojeva su dati ispod:

a = 0
b = 968
c = -14

print(type(a))
print(type(b))
print(type(c))

Izlaz:

<class 'int'>
<class 'int'>
<class 'int'>

Neke od ključnih razlika između celih brojeva i brojeva sa pokretnim zarezom u Python-u su:

Karakteristika Celi brojevi (*int*) Brojevi sa pokretnim zarezom (*float*)
Reprezentacija Prikazuju cele brojeve, njihove negativne parnjake i nulu, bez decimalnog dela. Prikazuju realne brojeve sa decimalnim zarezom.
Preciznost Neograničena preciznost, ne postoji ograničenje u pogledu dužine ili veličine *int* vrednosti. Jedino ograničenje je dostupna memorija. Ograničena preciznost. Najveća *float* vrednost je približno 1.8 x 10308.
Upotreba memorije Koristi manje memorije od *float* tipa. Koristi više memorije u poređenju sa celim brojevima.
Bitovske operacije Široko se koriste u bitovskim operacijama. Skoro se nikada ne koriste u bitovskim operacijama.
Primena Većina matematičkih operacija Većina matematičkih operacija

Različiti načini kreiranja i korišćenja *float* tipa u Python-u

Jedan od jednostavnih načina da počnemo sa radom sa *float* vrednostima u Python-u je da promenljivoj dodelimo *float* vrednost, na sledeći način:

# assign a variable a float value
a = 3.142

Drugi način da dobijemo *float* vrednost je da konvertujemo cele brojeve ili numeričke stringove u *float* tip pomoću konstruktora *float()*. Ako prosledimo ceo broj ili numerički string funkciji *float()*, on će biti konvertovan u *float* vrednost, kao što je prikazano u nastavku:

number1 = 2524
numString1 = "513.523"
numString2 = "1341"
# Convert to a float and store the float value in a variable
a = float(number1)
print(a)
b = float(numString1);
print(b)
c = float(numString2)
print(c)

Izlaz:

2524.0
513.523
1341.0

U gornjem primeru, celi broj i stringovi se konvertuju u *float* vrednost pomoću funkcije *float()*, a zatim se čuvaju u promenljivoj, koja se potom štampa, pokazujući rezultujuću vrednost sa pokretnim zarezom nakon konverzije.

Drugi način da dobijemo *float* vrednost je izvođenjem matematičkih operacija, kao što je deljenje, kao što je prikazano u nastavku:

num1 = 20
num2 = 3
result = num1/num2
print("Result of the division as an integer:")
print(int(20/3))
print("Result of the division as a float value:")
print(result)
print(type(result))

Izlaz:

Result of the division as an integer:
6
Result of the division as a float value:
6.666666666666667
<class 'float'>

U gornjem primeru, možemo primetiti da nam *float* vrednost daje tačniji rezultat u poređenju sa deljenjem i vraćanjem rezultata kao celog broja.

Prilikom rada sa *float* brojevima u Python-u, mogu se pojaviti neki neobični rezultati zbog načina na koji se *float* vrednosti predstavljaju interno u računaru. Brojevi sa pokretnim zarezom su prikazani u računarskom hardveru kao binarni (osnova 2) razlomci.

Međutim, većina decimalnih razlomaka, posebno onih sa ponavljajućim decimalama, ne mogu se prikazati kao tačan binarni razlomak. Zbog toga, brojevi sa pokretnim zarezom se obično čuvaju kao aproksimacija stvarne vrednosti.

Da bismo ovo praktično videli, uzmimo za primer *float* vrednost 0.3. Ako dodelite ovu vrednost promenljivoj, ona interno neće biti sačuvana kao tačno 0.3. Da bismo to uvideli, možemo upotrebiti funkciju *format()* da vidimo kako se 0.3 interno predstavlja. *format()* nam omogućava da prikažemo željeni broj značajnih cifara vrednosti sa kojima radimo. U primeru ispod, štampamo 0.3 do 20 značajnih cifara da vidimo kako se interno čuva.

num = 0.3
print("num to 20 significant figures")
print(format(num, '.20f'))
print("Value we stored for num")
print(num)

Izlaz:

num to 20 significant figures
0.29999999999999998890
Value we stored for num
0.3

Kao što vidimo, vrednost 0.3 koju smo dodelili promenljivoj pod nazivom *num*, interno nije sačuvana kao tačno 0.3. Kada odštampate promenljivu *num*, dobijate zaokruženu vrednost.

Zbog ove činjenice, mogu se dobiti neočekivani rezultati pri radu sa *float* vrednostima. Na primer, ako ručno izračunamo 0.3 + 0.3 + 0.3, dobićemo 0.9. Međutim, Python-u to nije slučaj jer interno čuva binarne aproksimacije stvarne vrednosti. To se vidi u primeru ispod:

sum = 0.3 + 0.3 + 0.3
answer = 0.9
print("Is sum equal to answer: ")
print(sum == answer)
print("The internal representation of of sum is: ")
print(sum)
print("The answer from manual calculation is: ")
print(answer)

Izlaz:

Is sum equal to answer: 
False
The internal representation of of sum is: 
0.8999999999999999
The answer from manual calculation is: 
0.9

Stoga, kada radimo sa *float* vrednostima, važno je imati na umu da Python ne čuva tačne vrednosti interno. Umesto toga, čuva približne vrednosti stvarne vrednosti.

Iz tog razloga, kada pravimo poređenja između *float* vrednosti, možda ćemo prvo želeti da zaokružimo te vrednosti na isti broj značajnih cifara. Za veću preciznost pri radu sa brojevima sa pokretnim zarezom u Python-u, razmislite o korišćenju ugrađenog modula *decimal*.

Decimalni modul u Python-u

U situacijama kada je visoka preciznost važna i obavezna, kao što su finansijski i naučni proračuni, korišćenje *float* tipa nije idealno. Da bi se garantovala visoka preciznost pri radu sa brojevima sa pokretnim zarezom, koristi se ugrađeni decimalni modul u Python-u.

Za razliku od *float* tipa, koji se čuva kao binarni prikaz sa pokretnim zarezom koji zavisi od mašine, decimalni modul čuva brojeve sa pokretnim zarezom koristeći mašinski nezavistan decimalni prikaz, što nudi veću preciznost.

Pored toga, decimalni modul može tačno da predstavi decimalne brojeve i da ih koristi u proračunima tačno onakve kakvi jesu. Takođe, nudi ispravno zaokruženu decimalnu aritmetiku sa pokretnim zarezom.

Da biste počeli da koristite decimalni modul, uvezite ga u svoju Python datoteku na sledeći način:

import decimal

Da bismo videli prednosti decimalnog modula, ponovimo ranije poređenje između zbira 0.3 + 0.3 + 0.3 i vrednosti 0.9. Kod za ovo je prikazan u nastavku:

import decimal

sum = decimal.Decimal('0.3') + decimal.Decimal('0.3') + decimal.Decimal('0.3')
answer = decimal.Decimal('0.9')
print("Is sum equal to answer: ")
print(sum == answer)
print("The internal representation of sum is: ")
print(sum)
print("The answer from manual calculation is: ")
print(answer)

Izlaz:

Is sum equal to answer: 
True
The internal representation of sum is: 
0.9
The answer from manual calculation is: 
0.9

Dakle, kada radite sa brojevima sa pokretnim zarezom i potrebna vam je visoka preciznost, uvek koristite decimalni modul.

Uobičajene greške pri radu sa *float* tipom

Mnoge greške koje se javljaju pri radu sa *float* tipom u Python-u proističu iz nerazumevanja načina na koji Python interno predstavlja brojeve sa pokretnim zarezom. Na primer, vrednost kao što je 0.3 neće biti sačuvana tačno kao 0.3. Zbog toga, moguće je da se pojave greške ako radite sa *float* vrednostima, pod pretpostavkom da su one sačuvane tačno onako kako su zadate.

Jedna od uobičajenih grešaka je greška zaokruživanja koja se javlja kada se vrše matematički proračuni sa *float* vrednostima. Pošto Python ne može da predstavi stvarne *float* vrednosti, mogu se pojaviti greške pri zaokruživanju, gde rezultati možda neće biti ono što se očekuje.

Zbog grešaka kao što su greške zaokruživanja, mogu se javiti greške kada se pokušava poređenje jednakosti između *float* vrednosti. Budite veoma pažljivi kada radite sa *float* tipom u Python-u i budite svesni neočekivanih rezultata.

Bolji način da izbegnete sve potencijalne greške pri radu sa *float* vrednostima je da koristite ugrađeni decimalni modul. Na taj način, rezultati vaših proračuna sa brojevima sa pokretnim zarezom biće predvidljiviji i tačniji.

Zaključak

Kao programer koji radi u Python-u, obavezno je korišćenje *float* tipa podataka. Da bi se izbegle greške sa ovim tipom podataka, važno je razumeti kako Python interno predstavlja brojeve sa pokretnim zarezom. Pošto Python ne može da čuva stvarne *float* brojeve, izbegavajte vršenje tačnih poređenja jednakosti sa *float* vrednostima, jer se u tom slučaju mogu javiti greške.

U slučajevima kada su vam potrebni tačni rezultati u aplikacijama, izbegavajte upotrebu *float* vrednosti. Umesto toga, koristite ugrađeni decimalni modul, koji pruža tačne rezultate sa brojevima sa pokretnim zarezom i predstavlja ih tačno onakvima kakvi jesu i to na mašinski nezavisan način.

Takođe možete pročitati o Python *itertools* funkcijama i Python *try/except* blokovima.