Veçoritë e Real Types, ose Kujdesuni me REAL

Pas publikimit artikullit për karakteristikat e tipizimit në PostgreSQL, komenti i parë ishte për vështirësitë në punën me numra realë. Vendosa të shfletoj shpejt kodin e pyetjeve SQL që kisha në dispozicion, për të parë se sa shpesh përdoret tipi REAL. Siç u duk, përdoret mjaft shpesh, dhe jo gjithmonë zhvilluesit e kuptojnë rreziqet që lidhen me të. Kjo ndodh edhe pse në internet dhe në Habrë ka mjaft artikuj të mirë për karakteristikat e ruajtjes së numrave real në memorien kompjuterike dhe punën me ata. Prandaj, në këtë artikull do të përpiqem të aplikoj këto karakteristika në PostgreSQL, dhe do të shqyrtoj me thjeshtësi problemet e lidhura me ta, në mënyrë që zhvilluesit e pyetjeve SQL të kenë më lehtë t'i shmangen atyre.

Dokumentacioni i PostgreSQL pĂ«rmban njĂ« frazĂ« tĂ« shkurtĂ«r: "Menaxhimi i kĂ«saj lloj gabimesh dhe pĂ«rhapja e tyre gjatĂ« llogaritjes Ă«shtĂ« njĂ« subjekt studimi i njĂ« seksioni tĂ« tĂ«rĂ« tĂ« matematikĂ«s dhe shkencĂ«s kompjuterike, dhe kĂ«tu nuk shqyrtohet" (duke e dĂ«rguar lexuesin me mençuri nĂ« standardin IEEE 754). ÇfarĂ« gabimesh bĂ«het fjalĂ« kĂ«tu? Le tĂ« diskutojmĂ« ato njĂ« nga njĂ«, dhe shpejt do tĂ« bĂ«het e qartĂ« pse unĂ« sĂ«rish kam marrĂ« penĂ«n pĂ«r tĂ« shkruar.

Marrim, për shembull, një pyetje të thjeshtë:

********* PYETJE *********
SELECT 0.1::REAL;
**************************
float4
--------
    0.1
(1 rresht)

Si rezultat, nuk do tĂ« shohim asgjĂ« tĂ« veçantĂ« – do tĂ« marrim 0.1 siç pritej. Por tani krahasojmĂ« atĂ« me 0.1:

********* PYETJE *********
SELECT 0.1::REAL = 0.1;
**************************
?kolona?
----------
f
(1 rresht)

Nuk janĂ« tĂ« barabarta! ÇfarĂ« mrekullish! Por kjo Ă«shtĂ« vetĂ«m fillimi. Disa mund tĂ« thonĂ«, e di qĂ« REAL sillet keq me thyesat, mirĂ«, unĂ« do tĂ« fus numra tĂ« tĂ«rĂ«, me ta gjithçka do tĂ« jetĂ« mirĂ«. Po, le tĂ« kthejmĂ« numrin 123 456 789 nĂ« tipin REAL:

********* PYETJE *********
SELECT 123456789::REAL::INT;
**************************
   int4   
-----------
123456792
(1 rresht)

Po, ai rezultoi më i madh me 3! Tani, databaza definitivisht harroi të llogarisë! Apo ndoshta ne nuk e kuptojmë diçka? Le të eci me hapat.

Së pari, le të kujtojmë disa gjëra themelore. Siç dihet, çdo numër decimal mund të shprehet me powers of ten. Kështu, numri 123.456 do të jetë i barabartë me 1*10^2 + 2*10^1 + 3*10^0 + 4*10^-1 + 5*10^-2 + 6*10^-3. Por kompjuteri punon me numra në formë binare, prandaj ata përshkruhen si shprehje me powers of two. Numri 5.625 në formë binare paraqitet si 101.101 dhe do të jetë i barabartë me 1*2^2 + 0*2^1 + 1*2^0 + 1*2^-1 + 0*2^-2 + 1*2^-3. Dhe nëse powers of two pozitive gjithmonë japin numra decimalë të plotë (1, 2, 4, 8, 16 etj.), me powers of two negative gjërat janë më të komplikuara (0.5, 0.25, 0.125, 0.0625 etj.). Problemi është që nuk çdo fraksion decimal mund të paraqitet si një fraksion binar të përfunduar. Kështu, e famshmja 0.1 në formën binare shfaqet si një vlerë periodike 0.0(0011). Si rezultat, vlera përfundimtare e këtij numri në memorien kompjuterike do të ndryshojë në varësi të saktësisë.

Tani Ă«shtĂ« koha pĂ«r tĂ« kujtuar si ruhen numrat realĂ« nĂ« memorien e kompjuterit. Duke folur nĂ« terma tĂ« pĂ«rgjithshĂ«m, njĂ« numĂ«r real pĂ«rbĂ«het nga tri komponentĂ« kryesore – shenja, mantisa, dhe eksponenti. Shenja mund tĂ« jetĂ« ose pozitive, ose negative, prandaj i kushtohet njĂ« bit. SidoqoftĂ«, numri i bitĂ«ve tĂ« mantisĂ«s dhe eksponentĂ«ve pĂ«rcaktohet nga lloji i numrit real. KĂ«shtu, pĂ«r llojin REAL, gjatĂ«sia e mantisĂ«s Ă«shtĂ« 23 bitĂ« (njĂ« bit, i barabartĂ« me 1, nĂ« mĂ«nyrĂ« qĂ« tĂ« shtohet nĂ« fillim tĂ« mantisĂ«s, dhe bĂ«het 24), dhe eksponenti – 8 bitĂ«. NĂ« total, kemi 32 bitĂ«, ose 4 byte. NdĂ«rsa pĂ«r llojin DOUBLE PRECISION, gjatĂ«sia e mantisĂ«s Ă«shtĂ« 52 bitĂ«, dhe eksponenti – 11 bitĂ«, duke bĂ«rĂ« gjithsej 64 bitĂ«, ose 8 byte. PostgreSQL nuk mbĂ«shtet mĂ« shumĂ« saktĂ«si pĂ«r numrat me lĂ«vizje tĂ« pikave.

Le tĂ« paketojmĂ« numrin tonĂ« 0.1 nĂ« formĂ«n dekimale nĂ« tĂ« dy llojet – REAL dhe DOUBLE PRECISION. Duke qenĂ« se shenja dhe vlera e eksponentit tek ne pĂ«rputhen, do t'i kushtojmĂ« vĂ«mendje mantisĂ«s (qĂ« po qĂ«llimisht po e lĂ« pas dore karakteristikĂ«n jo shumĂ« tĂ« dukshme tĂ« ruajtjes sĂ« vlerave tĂ« eksponentit dhe numrave realĂ« zero, pasi kĂ«to ndĂ«rlikojnĂ« kuptimin dhe shpĂ«rqendrojnĂ« nga thelbi i problemit, nĂ«se doni tĂ« dini mĂ« shumĂ« – shihni standardin IEEE 754). ÇfarĂ« do tĂ« marrim? NĂ« rreshtin e sipĂ«rm do tĂ« paraqes "mantisĂ«n" pĂ«r llojin REAL (duke e marrĂ« parasysh rrumbullakimin e bitit tĂ« fundit nĂ« 1 nĂ« numrin mĂ« tĂ« afĂ«rt tĂ« paraqitshĂ«m, pĂ«rndryshe do tĂ« del 0.099999...), ndersa nĂ« rreshtin e poshtĂ«m – pĂ«r llojin DOUBLE PRECISION:

0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001

Sigurisht, kĂ«to janĂ« dy numra tĂ«rĂ«sisht tĂ« ndryshĂ«m! Prandaj, kur krahasojmĂ«, numri i parĂ« do tĂ« plotĂ«sohet me zero dhe, si rezultat, do tĂ« jetĂ« mĂ« i madh se numri i dytĂ« (duke marrĂ« parasysh rrumbullakimin – tĂ« shĂ«nuarit nĂ« tĂ« trashĂ« njĂ«si). Kjo e shpjegon paqartĂ«sinĂ« nga shembujt tanĂ«. NĂ« shembullin e dytĂ«, numri i shprehur qartĂ« 0.1 konvertohet nĂ« llojin DOUBLE PRECISION, pas sĂ« cilĂ«s krahasohet me numrin e llojit REAL. TĂ« dy konvertohen nĂ« tĂ« njĂ«jtin tip, dhe kemi pikĂ«risht atĂ« qĂ« shohim mĂ« sipĂ«r. Le tĂ« ndryshojmĂ« pyetjen, qĂ« gjithçka tĂ« vendoset nĂ« vendin e vet:

********* KËRKESË *********
SELECT 0.1::REAL > 0.1::DOUBLE PRECISION;
**************************
?column?
----------
t
(1 rresht)

Dhe në të vërtetë, duke e ekzekutuar dyfish konvertimin e numrit 0.1 në REAL dhe DOUBLE PRECISION, marrim përgjigjen për enigmën:

********* KËRKESË *********
SELECT 0.1::REAL::DOUBLE PRECISION;
**************************

      float8       
-------------------
0.100000001490116
(1 rresht)

Kjo gjithashtu shpjegon shembullin e tretë nga ata të përmendur më sipër. Numri 123 456 789 thjesht nuk mund të vendoset në 24 bita të mantisës (23 të shfaqura + 1 të nënkuptuar). Numri maksimale i plotë që mund të vendoset në 24 bita do të jetë 224-1 = 16 777 215. Prandaj, numri ynë 123 456 789 rroundohet në 123 456 792. Duke ndryshuar tipin në DOUBLE PRECISION, nuk do ta shohim më një skenar të tillë:

********* KËRKESË *********
SELECT 123456789::DOUBLE PRECISION::INT;
**************************
   int4   
-----------
123456789
(1 rresht)

Kjo është gjithçka. Zakonisht, nuk ka asnjë mrekulli. Por gjithçka e përshkruar është një arsye e mirë për të menduar se sa e nevojshme është vërtet lloji REAL. Ndoshta, përfitimi më i madh nga përdorimi i tij është shpejtësia e llogaritjeve me një humbje të njohur saktësie. Por a do të ishte ky një skenar universal që justifikon përdorimin e tillë të shpeshtë të këtij tipi? Nuk mendoj.

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster