Benetako motaren ezaugarri irrealak, edo kontuz ibili REAL-ekin

Argitaratu ondoren Artikulua PostgreSQL-n idaztearen ezaugarriei buruz, lehen iruzkina zenbaki errealekin lan egiteko zailtasunei buruzkoa izan zen. Eskura nituen SQL kontsulten kodeari begirada azkar bat ematea erabaki nuen REAL mota zenbateraino erabiltzen duten ikusteko. Bihurtzen da askotan erabiltzen dela eta garatzaileek ez dituzte beti ulertzen horren atzean dauden arriskuak. Eta hau Interneten eta HabrΓ©-n artikulu on asko egon arren ordenagailuaren memorian zenbaki errealak gordetzearen ezaugarriei buruz eta haiekin lan egiteari buruz. Hori dela eta, artikulu honetan PostgreSQL-ri horrelako ezaugarriak aplikatzen saiatuko naiz, eta horiekin lotutako arazoei begirada azkar bat ematen saiatuko naiz, SQL kontsultaren garatzaileek errazagoa izan dezaten horiek saihestea.

PostgreSQL dokumentazioak labur-labur dio: "Horrelako akatsen kudeaketa eta konputazioan zehar hedatzea matematika eta informatika adar oso baten gaia da, eta ez da hemen lantzen" (irakurlea IEEE 754 estandarra zentzuz aipatzen duen bitartean). Zer motatako akatsak esan nahi dira hemen? Eztabaidatu ditzagun ordenan, eta laster argituko da zergatik hartu nuen berriro boligrafoa.

Har dezagun adibidez eskaera sinple bat:

********* Π—ΠΠŸΠ ΠžΠ‘ *********
SELECT 0.1::REAL;
**************************
float4
--------
    0.1
(1 строка)

Ondorioz, ez dugu ezer berezirik ikusiko; esperotako 0.1 lortuko dugu. Baina orain aldera dezagun 0.1-ekin:

********* Π—ΠΠŸΠ ΠžΠ‘ *********
SELECT 0.1::REAL = 0.1;
**************************
?column?
----------
f
(1 строка)

Ez berdin! Zein mirariak! Baina harago, gehiago. Norbaitek esango du: badakit ERREALA zatikiekin gaizki jokatzen duela, beraz, zenbaki osoak sartuko ditut bertan, eta dena ondo egongo da, zalantzarik gabe. Ados, eman dezagun 123 zenbakia REALera:

********* Π—ΠΠŸΠ ΠžΠ‘ *********
SELECT 123456789::REAL::INT;
**************************
   int4   
-----------
123456792
(1 строка)

Eta 3 gehiago izan ziren! Hori da, datu-baseak azkenean nola zenbatu ahaztu du! Edo zerbait gaizki ulertzen ari gara? Asma dezagun.

Lehenik eta behin, gogora dezagun materiala. Dakizuenez, edozein zenbaki hamartar hamarreko potentziatan heda daiteke. Beraz, 123.456 zenbakia 1*102 + 2*101 + 3*100 + 4*10-1 + 5*10-2 + ​​​​6*10-3 berdina izango da. Baina ordenagailuak zenbakiekin funtzionatzen du forma bitarrean, beraz, bi potentziaren hedapen moduan irudikatu behar dira. Beraz, 5.625 zenbakia bitarrean 101.101 gisa irudikatzen da eta 1*22 + 0*21 + 1*20 + 1*2-1 + 0*2-2 + 1*2-3 berdina izango da. Eta biren potentzia positiboek beti zenbaki hamartar osoak ematen badituzte (1, 2, 4, 8, 16, etab.), orduan negatiboekin dena zailagoa da (0.5, 0.25, 0.125, 0,0625, etab.). Arazoa hori da Hamartar guztiak ezin dira zatiki bitar finitu gisa irudikatu. Beraz, gure 0.1 ezaguna zati bitar baten moduan 0.0(0011) balio periodiko gisa agertzen da. Ondorioz, zenbaki horren azken balioa ordenagailuaren memorian aldatuko da bit-sakoneraren arabera.

Zenbaki errealak ordenagailuaren memorian nola gordetzen diren gogoratzeko unea da. Oro har, zenbaki erreal batek hiru zati nagusi ditu: zeinua, mantisa eta berretzailea. Zeinua plus edo minus izan daiteke, beraz, bit bat esleitzen zaio. Baina mantisaren eta berretzailearen bit kopurua benetako motaren arabera zehazten da. Beraz, REAL motarako, mantisaren luzera 23 bit da (mantisaren hasieran inplizituki 1 adina bit bat gehitzen da, eta emaitza 24 da), eta berretzailea 8 bit da. Guztira 32 bit edo 4 byte da. Eta DOUBLE PRECISION motarako, mantisaren luzera 52 bit izango da, eta berretzailea 11 bit, guztira 64 bit, edo 8 byte. PostgreSQL-k ez du zehaztasun handiagoa onartzen koma mugikorreko zenbakietarako.

Jar ditzagun gure 0.1 zenbaki hamartarra ERREAL eta DOIZTASUN BIKOITZA motatan. Berretzailearen zeinua eta balioa berdinak direnez, mantisan zentratuko gara (nahita alde batera uzten ditut berretzailearen balioak eta zero balio errealak gordetzeko agerikoak ez diren ezaugarriak, ulermena zailtzen baitute eta esentziatik aldendu egiten baitute. arazoaren inguruan, interesa izanez gero, ikus IEEE 754 estandarra). Zer lortuko dugu? Goiko lerroan ERREAL motarako "mantissa" emango dut (azken bitaren biribilketa kontuan hartuta 1 zenbaki irudikagarri hurbilenera, bestela 0.099999 izango da...), eta beheko lerroan - ZEHAZTASUN BIKOITZA mota:

0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001

Argi dago bi zenbaki guztiz desberdinak direla! Beraz, alderatzean, lehenengo zenbakia zeroz beteko da eta, beraz, bigarrena baino handiagoa izango da (biribilketa kontuan hartuta - lodiz markatutakoa). Honek gure adibideetatik anbiguotasuna azaltzen du. Bigarren adibidean, esplizituki zehaztutako 0.1 zenbakia ZEHAZTASUN BIKOITZA motara botatzen da, eta, ondoren, ERREAL motako zenbaki batekin alderatzen da. Biak mota berera murrizten dira, eta goian ikusten duguna dugu. Alda dezagun kontsulta, dena bere tokian egon dadin:

********* Π—ΠΠŸΠ ΠžΠ‘ *********
SELECT 0.1::REAL > 0.1::DOUBLE PRECISION;
**************************
?column?
----------
t
(1 строка)

Eta hain zuzen ere, 0.1 zenbakiaren murrizketa bikoitza REAL eta ZEHAZTASUN BIKOITZAra eginez, asmakizunaren erantzuna lortuko dugu:

********* Π—ΠΠŸΠ ΠžΠ‘ *********
SELECT 0.1::REAL::DOUBLE PRECISION;
**************************

      float8       
-------------------
0.100000001490116
(1 строка)

Honek goiko hirugarren adibidea ere azaltzen du. 123 zenbakia sinplea da ezinezkoa da mantisa 24 bitetan egokitzea (23 esplizitua + 1 inplizitua). 24 bitetan sar daitekeen zenbaki oso maximoa 224-1 = 16 da. Hori dela eta, gure 777 zenbakia hurbilen dagoen 215 irudikagarrira biribiltzen da. Mota ZEHAZTASUN BIKOITZAra aldatuz gero, ez dugu egoera hau ikusten:

********* Π—ΠΠŸΠ ΠžΠ‘ *********
SELECT 123456789::DOUBLE PRECISION::INT;
**************************
   int4   
-----------
123456789
(1 строка)

Hori da dena. Miraririk ez dagoela ematen du. Baina deskribatutako guztia arrazoi ona da REAL mota benetan zenbat behar duzun pentsatzeko. Beharbada erabileraren abantailarik handiena zehaztasun galera ezaguna duten kalkuluen abiadura da. Baina hori izango al litzateke mota hau maiz erabiltzea justifikatuko lukeen eszenatoki unibertsala? Ez pentsa.

Iturria: www.habr.com

Gehitu iruzkin berria