PÄc publicÄÅ”anas
PostgreSQL dokumentÄcijÄ ir Ä«si teikts: āÅ Ädu kļūdu pÄrvaldÄ«ba un to izplatÄ«Å”anÄs aprÄÄ·inu laikÄ ir veselas matemÄtikas un datorzinÄtÅu nozares priekÅ”mets, un tas Å”eit nav aplÅ«kotsā (lai gan lasÄ«tÄjs gudri atsaucas uz IEEE 754 standartu). KÄdas kļūdas Å”eit ir domÄtas? ApspriedÄ«sim tos secÄ«bÄ, un drÄ«z kļūs skaidrs, kÄpÄc es atkal ÅÄmu rokÄs pildspalvu.
Å emsim, piemÄram, vienkÄrÅ”u pieprasÄ«jumu:
********* ŠŠŠŠ ŠŠ” *********
SELECT 0.1::REAL;
**************************
float4
--------
0.1
(1 ŃŃŃŠ¾ŠŗŠ°)
RezultÄtÄ neko Ä«paÅ”u neredzÄsim ā iegÅ«sim gaidÄ«to 0.1. Bet tagad salÄ«dzinÄsim to ar 0.1:
********* ŠŠŠŠ ŠŠ” *********
SELECT 0.1::REAL = 0.1;
**************************
?column?
----------
f
(1 ŃŃŃŠ¾ŠŗŠ°)
Nav vienÄds! KÄdi brÄ«numi! Bet tÄlÄk, vairÄk. KÄds teiks: es zinu, ka REAL slikti uzvedas ar daļskaitļiem, tÄpÄc es ievadÄ«Å”u tur veselus skaitļus, un ar tiem viss noteikti bÅ«s kÄrtÄ«bÄ. Labi, ievadÄ«sim skaitli 123 456 789 uz REAL:
********* ŠŠŠŠ ŠŠ” *********
SELECT 123456789::REAL::INT;
**************************
int4
-----------
123456792
(1 ŃŃŃŠ¾ŠŗŠ°)
Un izrÄdÄ«jÄs, ka ir vÄl 3! Tas tÄ, datubÄze beidzot ir aizmirsusi, kÄ skaitÄ«t! Vai arÄ« mÄs kaut ko pÄrpratÄm? IzdomÄsim.
PirmkÄrt, atcerÄsimies materiÄlu. KÄ jÅ«s zinÄt, jebkuru decimÄlo skaitli var izvÄrst desmit pakÄpÄs. TÄtad skaitlis 123.456 bÅ«s vienÄds ar 1*102 + 2*101 + 3*100 + 4*10-1 + 5*10-2 + āāāā6*10-3. Bet dators darbojas ar skaitļiem binÄrÄ formÄ, tÄpÄc tie ir jÄattÄlo izvÄrsuma veidÄ ar diviem pakÄpÄm. TÄpÄc skaitlis 5.625 binÄrajÄ formÄ tiek attÄlots kÄ 101.101 un bÅ«s vienÄds ar 1*22 + 0*21 + 1*20 + 1*2-1 + 0*2-2 + 1*2-3. Un, ja divu pozitÄ«vÄs pakÄpes vienmÄr dod veselus skaitļus aiz komata (1, 2, 4, 8, 16 utt.), tad ar negatÄ«vajiem viss ir sarežģītÄk (0.5, 0.25, 0.125, 0,0625 utt.). ProblÄma ir tÄda Ne katru decimÄldaļu var attÄlot kÄ ierobežotu binÄru daļu. TÄdÄjÄdi mÅ«su bÄdÄ«gi slavenais 0.1 binÄras daļas formÄ parÄdÄs kÄ periodiskÄ vÄrtÄ«ba 0.0(0011). LÄ«dz ar to Ŕī skaitļa galÄ«gÄ vÄrtÄ«ba datora atmiÅÄ mainÄ«sies atkarÄ«bÄ no bitu dziļuma.
Tagad ir pienÄcis laiks atcerÄties, kÄ reÄlie skaitļi tiek saglabÄti datora atmiÅÄ. VispÄrÄ«gi runÄjot, reÄls skaitlis sastÄv no trim galvenajÄm daļÄm - zÄ«mes, mantisas un eksponenta. ZÄ«me var bÅ«t vai nu plus, vai mÄ«nus, tÄpÄc tam tiek atvÄlÄts viens bits. Bet mantisas un eksponenta bitu skaitu nosaka reÄlais tips. TÄtad REAL tipam mantisas garums ir 23 biti (mantisas sÄkumam netieÅ”i tiek pievienots viens bits, kas vienÄds ar 1, un rezultÄts ir 24), un eksponents ir 8 biti. KopÄ ir 32 biti jeb 4 baiti. Un DOUBLE PRECISION tipam mantisas garums bÅ«s 52 biti, un eksponents bÅ«s 11 biti, kopÄ 64 biti jeb 8 baiti. PostgreSQL neatbalsta augstÄku peldoÅ”Ä komata skaitļu precizitÄti.
Iepakosim savu decimÄlskaitli 0.1 gan REAL, gan DOUBLE PRECISION veidos. TÄ kÄ eksponenta zÄ«me un vÄrtÄ«ba ir vienÄda, mÄs koncentrÄsimies uz mantisu (es apzinÄti izlaižu nepÄrprotamÄs eksponenta vÄrtÄ«bu un nulles reÄlÄs vÄrtÄ«bas saglabÄÅ”anas iezÄ«mes, jo tÄs sarežģī izpratni un novÄrÅ” uzmanÄ«bu no bÅ«tÄ«bas Ja interesÄ, skatiet IEEE 754 standartu). Ko mÄs iegÅ«sim? AugÅ”ÄjÄ rindÄ doÅ”u āmantisuā ÄŖSTAM tipam (Åemot vÄrÄ pÄdÄjÄ bita noapaļoÅ”anu par 1 lÄ«dz tuvÄkajam attÄlojamajam skaitlim, pretÄjÄ gadÄ«jumÄ tas bÅ«s 0.099999...), bet apakÅ”ÄjÄ rindÄ - par. DOUBLE PRECISION tips:
0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001
AcÄ«mredzot tie ir divi pilnÄ«gi atŔķirÄ«gi skaitļi! TÄpÄc, salÄ«dzinot, pirmais cipars tiks polsterÄts ar nullÄm un lÄ«dz ar to bÅ«s lielÄks par otro (Åemot vÄrÄ noapaļoÅ”anu ā treknrakstÄ atzÄ«mÄtais). Tas izskaidro mÅ«su piemÄru neskaidrÄ«bu. OtrajÄ piemÄrÄ skaidri norÄdÄ«tais skaitlis 0.1 tiek nodots DOUBLE PRECISION tipam un pÄc tam salÄ«dzinÄts ar ÄŖSTÄ tipa skaitli. Abi ir samazinÄti lÄ«dz tÄdam paÅ”am tipam, un mums ir tieÅ”i tas, ko mÄs redzam iepriekÅ”. ModificÄsim vaicÄjumu, lai viss nonÄktu savÄs vietÄs:
********* ŠŠŠŠ ŠŠ” *********
SELECT 0.1::REAL > 0.1::DOUBLE PRECISION;
**************************
?column?
----------
t
(1 ŃŃŃŠ¾ŠŗŠ°)
Un tieÅ”Äm, veicot skaitļa 0.1 dubultu samazinÄÅ”anu uz REÄLU un DUBULU PRECIZITÄTI, mÄs iegÅ«stam atbildi uz mÄ«klu:
********* ŠŠŠŠ ŠŠ” *********
SELECT 0.1::REAL::DOUBLE PRECISION;
**************************
float8
-------------------
0.100000001490116
(1 ŃŃŃŠ¾ŠŗŠ°)
Tas arÄ« izskaidro treÅ”o piemÄru iepriekÅ”. Skaitlis 123 456 789 ir vienkÄrÅ”s mantisu nav iespÄjams ievietot 24 bitos (23 tieÅ”i + 1 netieÅ”s). MaksimÄlais veselais skaitlis, ko var ietilpt 24 bitos, ir 224-1 = 16 777 215. TÄpÄc mÅ«su skaitlis 123 456 789 ir noapaļots lÄ«dz tuvÄkajam reprezentÄjamajam 123 456 792. Mainot veidu uz DOUBLE PRECISION, mÄs vairs neredzam Å”o scenÄriju:
********* ŠŠŠŠ ŠŠ” *********
SELECT 123456789::DOUBLE PRECISION::INT;
**************************
int4
-----------
123456789
(1 ŃŃŃŠ¾ŠŗŠ°)
Tas ir viss. IzrÄdÄs, ka brÄ«numu nav. Bet viss aprakstÄ«tais ir labs iemesls padomÄt par to, cik ļoti jums patieÅ”Äm ir vajadzÄ«gs ÄŖSTAIS tips. IespÄjams, ka lielÄkÄ tÄ izmantoÅ”anas priekÅ”rocÄ«ba ir aprÄÄ·inu Ätrums ar zinÄmu precizitÄtes zudumu. Bet vai tas bÅ«tu universÄls scenÄrijs, kas attaisnotu tik biežu Å”Äda veida izmantoÅ”anu? NedomÄjiet.
Avots: www.habr.com