Чыныгы түрлөрдүн реалдуу эмес өзгөчөлүктөрү, же REAL менен этият болуңуз

жарыялангандан кийин макалалар PostgreSQLде терүүнүн өзгөчөлүктөрү жөнүндө, эң биринчи комментарий чыныгы сандар менен иштөөнүн кыйынчылыктары жөнүндө болду. Мен алар REAL түрүн канчалык көп колдонорун көрүү үчүн мага жеткиликтүү SQL сурамдарынын кодун тез карап чыгууну чечтим. Көрсө, ал көп колдонулат жана иштеп чыгуучулар анын артында кандай коркунуч бар экенин дайыма эле түшүнө беришпейт. Бул Интернетте жана Хабреде компьютердин эсинде реалдуу сандарды сактоонун өзгөчөлүктөрү жана алар менен иштөө жөнүндө көптөгөн жакшы макалалар бар экендигине карабастан. Ошондуктан, бул макалада мен мындай функцияларды PostgreSQLге колдонууга аракет кылам жана алар менен байланышкан көйгөйлөрдү тез карап чыгууга аракет кылам, андыктан SQL сурамдарын иштеп чыгуучулар аларды болтурбоо оңой болот.

PostgreSQL документациясында кыскача мындай деп айтылат: "Мындай каталарды башкаруу жана аларды эсептөө учурунда жайылтуу математиканын жана информатиканын бүтүндөй бир тармагынын предмети болуп саналат жана бул жерде каралбайт" (окуучуну IEEE 754 стандартына акылмандык менен шилтеме кылуу менен). Бул жерде кандай каталар айтылууда? Аларды ирети менен талкуулайлы, эмне үчүн кайра калем алганым жакында билинет.

Мисалы, жөнөкөй өтүнүчтү алалы:

********* ЗАПРОС *********
SELECT 0.1::REAL;
**************************
float4
--------
    0.1
(1 строка)

Натыйжада, биз өзгөчө эч нерсе көрө албайбыз - биз күтүлгөн 0.1 аласыз. Бирок азыр аны 0.1 менен салыштырып көрөлү:

********* ЗАПРОС *********
SELECT 0.1::REAL = 0.1;
**************************
?column?
----------
f
(1 строка)

Барабар эмес! Кандай кереметтер! Бирок андан ары, көбүрөөк. Кимдир бирөө айтат, мен REAL бөлчөктөр менен өзүн жаман алып барарын билем, ошондуктан мен ал жерге бүтүн сандарды киргизем, алар менен баары жакшы болот. Макул, келгиле 123 456 789 санын REALга чыгаралы:

********* ЗАПРОС *********
SELECT 123456789::REAL::INT;
**************************
   int4   
-----------
123456792
(1 строка)

Жана дагы 3 болуп чыкты! Болду, маалымат базасы кантип санаганды унутуп калды! Же биз бир нерсени туура эмес түшүнүп жатабызбы? Келгиле, аны аныктап көрөлү.

Биринчиден, материалды эстеп көрөлү. Белгилүү болгондой, каалаган ондук сан ондук даражаларга чейин кеңейтилиши мүмкүн. Ошентип, 123.456 саны 1*102 + 2*101 + 3*100 + 4*10-1 + 5*10-2 + ​​6*10-3 ге барабар болот. Бирок компьютер экилик формадагы сандар менен иштейт, ошондуктан алар эки даражанын кеңейүү түрүндө көрсөтүлүшү керек. Демек, бинардык системада 5.625 саны 101.101 катары көрсөтүлөт жана 1*22 + 0*21 + 1*20 + 1*2-1 + 0*2-2 + 1*2-3 барабар болот. Ал эми экинин оң даражалары ар дайым бүтүн ондук сандарды берсе (1, 2, 4, 8, 16 ж.б.), анда терс сандар менен баары татаалыраак (0.5, 0.25, 0.125, 0,0625 ж.б.). Маселе мына ушунда Ар бир ондук чектүү экилик бөлчөк катары берилиши мүмкүн эмес. Ошентип, экилик бөлчөк түрүндөгү биздин белгилүү 0.1 мезгилдик маани 0.0(0011) катары пайда болот. Демек, бул сандын компьютер эсиндеги акыркы мааниси бит тереңдигине жараша өзгөрүп турат.

Азыр чыныгы сандар компьютердин эсинде кандайча сакталарын эстеп калуу учуру. Жалпысынан алганда, реалдуу сан үч негизги бөлүктөн турат - белги, мантисса жана көрсөткүч. Белги плюс же минус болушу мүмкүн, ошондуктан ага бир бит бөлүнөт. Бирок мантисса жана экспоненттин биттеринин саны чыныгы түрү менен аныкталат. Ошентип, REAL түрү үчүн мантиссанын узундугу 23 бит (1ге барабар бир бит мантиссанын башына кыйыр түрдө кошулат, натыйжада 24), көрсөткүчү 8 бит. Жалпысынан 32 бит же 4 байт. Ал эми DOUBLE PRECISION түрү үчүн мантиссанын узундугу 52 бит, ал эми көрсөткүчү 11 бит, жалпысынан 64 бит же 8 байт болот. PostgreSQL калкыма чекиттердин жогорку тактыгын колдобойт.

Келгиле, ондук саныбызды 0.1 РЕАЛ жана КОШ ТАКТЫК түрлөрүнө топтоп көрөлү. Көрсөткүчтүн белгиси менен мааниси бирдей болгондуктан, биз мантиссага токтолобуз (мен экспоненттин маанилерин сактоонун ачык эмес өзгөчөлүктөрүн жана нөлдүк реалдуу маанилерди атайылап өткөрүп жиберем, анткени алар түшүнүүнү татаалдаштырып, маңызынан алаксытат. Эгер кызыкдар болсо, IEEE 754 стандартын караңыз). Биз эмне алабыз? Жогорку сапта REAL түрү үчүн "мантиссаны" берем (акыркы биттин 1ге тегеректөөсүн эске алуу менен, антпесе ал 0.099999 болот...), ал эми төмөнкү сапта - үчүн DOUBLE RECISION түрү:

0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001

Албетте, бул эки башка сандар! Ошондуктан, салыштырганда, биринчи сан нөлдөр менен толтурулат, демек, экинчиден чоңураак болот (тегеректөөнү эске алуу менен - ​​кара тамга менен белгиленген). Бул биздин мисалдардагы түшүнүксүздүктү түшүндүрөт. Экинчи мисалда, 0.1 так көрсөтүлгөн саны DOUBLE PRECSISION түрүнө чыгарылып, андан кийин REAL түрүнүн саны менен салыштырылат. Экөө тең бир түргө кыскартылган жана биз жогоруда көргөн нерсебизге ээбиз. Келгиле, суроону баары ордуна келгидей кылып өзгөртөлү:

********* ЗАПРОС *********
SELECT 0.1::REAL > 0.1::DOUBLE PRECISION;
**************************
?column?
----------
t
(1 строка)

Чынында эле, 0.1 санын REAL жана КОШ ТАКТЫЛЫКка эки эсе кыскартуу менен, табышмактын жообун алабыз:

********* ЗАПРОС *********
SELECT 0.1::REAL::DOUBLE PRECISION;
**************************

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

Бул да жогорудагы үчүнчү мисалды түшүндүрөт. 123 456 789 саны жөнөкөй мантисаны 24 битке батыруу мүмкүн эмес (23 ачык + 1 кыйыр). 24 битке бата турган максималдуу бүтүн сан 224-1 = 16 777 215. Демек, биздин 123 456 789 саны жакынкы 123 456 792ге тегеректелген. Типти DOUBLE PRECISION кылып өзгөртүү менен, биз мындан ары бул сценарийди көрбөйбүз:

********* ЗАПРОС *********
SELECT 123456789::DOUBLE PRECISION::INT;
**************************
   int4   
-----------
123456789
(1 строка)

Баары болду. Көрсө, эч кандай керемет жок. Бирок сүрөттөлгөн нерселердин баары сизге REAL түрү канчалык керек экендиги жөнүндө ойлонууга жакшы себеп. Балким, аны колдонуунун эң чоң артыкчылыгы - белгилүү тактык жоготуу менен эсептөөлөрдүн ылдамдыгы. Бирок бул типти көп колдонууну актай турган универсалдуу сценарий болобу? Ойлобойм.

Source: www.habr.com

Комментарий кошуу