Haqiqiy turlarning haqiqiy bo'lmagan xususiyatlari yoki REAL bilan ehtiyot bo'ling

Nashrdan keyin maqolalar PostgreSQL-da yozish xususiyatlari haqida birinchi sharh haqiqiy raqamlar bilan ishlashning qiyinchiliklari haqida edi. REAL turidan qanchalik tez-tez foydalanishini ko'rish uchun men uchun mavjud bo'lgan SQL so'rovlarining kodini tezda ko'rib chiqishga qaror qildim. Ma'lum bo'lishicha, u juda tez-tez ishlatiladi va ishlab chiquvchilar har doim ham uning ortida turgan xavf-xatarni tushunmaydilar. Va bu Internetda va Habré-da haqiqiy raqamlarni kompyuter xotirasida saqlash xususiyatlari va ular bilan ishlash haqida juda ko'p yaxshi maqolalar mavjudligiga qaramay. Shuning uchun, ushbu maqolada men PostgreSQL-ga bunday xususiyatlarni qo'llashga harakat qilaman va ular bilan bog'liq muammolarni tezda ko'rib chiqishga harakat qilaman, shunda SQL so'rovlarini ishlab chiquvchilar ulardan qochishlari osonroq bo'ladi.

PostgreSQL hujjatlarida qisqacha shunday deyilgan: “Bunday xatolarni boshqarish va hisoblash jarayonida ularning tarqalishi matematika va informatikaning butun bir sohasi boʻlib, bu yerda yoritilgan emas” (oʻquvchini IEEE 754 standartiga donolik bilan murojaat qilgan holda). Bu erda qanday xatolar nazarda tutilgan? Keling, ularni tartibda muhokama qilaylik, nega yana qalam olganim tez orada oydinlashadi.

Masalan, oddiy so'rovni olaylik:

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

Natijada, biz hech qanday maxsus narsani ko'rmaymiz - biz kutilgan 0.1 ni olamiz. Ammo endi uni 0.1 ga solishtiramiz:

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

Teng emas! Qanday mo''jizalar! Ammo bundan keyin, ko'proq. Kimdir aytadi, men REAL kasrlar bilan yomon ish tutishini bilaman, shuning uchun men u erga butun raqamlarni kiritaman va ular bilan hammasi yaxshi bo'ladi. Mayli, keling, 123 456 789 raqamini REALga chiqaramiz:

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

Va yana 3 ta bo'lib chiqdi! Mana, ma'lumotlar bazasi nihoyat hisoblashni unutdi! Yoki biz nimanidir noto'g'ri tushunyapmizmi? Keling, buni aniqlaylik.

Birinchidan, materialni eslaylik. Ma'lumki, har qanday o'nlik sonni o'n darajaga kengaytirish mumkin. Shunday qilib, 123.456 raqami 1 * 102 + 2 * 101 + 3 * 100 + 4 * 10-1 + 5 * 10-2 + ​​6 * 10-3 ga teng bo'ladi. Ammo kompyuter ikkilik ko'rinishdagi raqamlar bilan ishlaydi, shuning uchun ular ikkita kuchda kengayish shaklida ifodalanishi kerak. Shuning uchun ikkilik tizimda 5.625 raqami 101.101 sifatida ifodalanadi va 1*22 + 0*21 + 1*20 + 1*2-1 + 0*2-2 + 1*2-3 ga teng boʻladi. Va agar ikkita musbat kuchlar har doim butun o'nlik sonlarni (1, 2, 4, 8, 16 va boshqalar) bersa, unda salbiy bilan hamma narsa murakkabroq (0.5, 0.25, 0.125, 0,0625 va boshqalar). Muammo shundaki Har bir o'nlik sonli ikkilik kasr sifatida ifodalanishi mumkin emas. Shunday qilib, ikkilik kasr ko'rinishidagi mashhur 0.1 0.0(0011) davriy qiymat sifatida paydo bo'ladi. Binobarin, bu raqamning kompyuter xotirasidagi yakuniy qiymati bit chuqurligiga qarab o'zgaradi.

Endi haqiqiy raqamlar kompyuter xotirasida qanday saqlanganligini eslash vaqti keldi. Umuman olganda, haqiqiy son uchta asosiy qismdan iborat - belgi, mantis va ko'rsatkich. Belgisi ortiqcha yoki minus bo'lishi mumkin, shuning uchun unga bir bit ajratiladi. Ammo mantis va eksponentning bitlari soni haqiqiy turga qarab belgilanadi. Shunday qilib, REAL turi uchun mantisaning uzunligi 23 bit (1 ga teng bir bit mantisaning boshiga bilvosita qo'shiladi va natija 24), ko'rsatkich esa 8 bit. Jami 32 bit yoki 4 bayt. DOUBLE PRECISION turi uchun esa mantisaning uzunligi 52 bit, eksponent esa 11 bit, jami 64 bit yoki 8 bayt bo'ladi. PostgreSQL suzuvchi nuqtali raqamlar uchun yuqori aniqlikni qo'llab-quvvatlamaydi.

Keling, o'nlik kasr sonimizni 0.1 REAL va DOUBLE PRECISION turlariga to'playmiz. Ko'rsatkichning belgisi va qiymati bir xil bo'lganligi sababli, biz mantisaga e'tibor qaratamiz (men ko'rsatkich qiymatlarini saqlashning noaniq xususiyatlarini va nol haqiqiy qiymatlarni ataylab o'tkazib yuboraman, chunki ular tushunishni murakkablashtiradi va mohiyatdan chalg'itadi. Muammo haqida, agar qiziqsangiz, IEEE 754 standartiga qarang). Biz nima olamiz? Yuqori qatorda men REAL turi uchun "mantissa" ni beraman (oxirgi bitni 1 ga yaxlitlashni hisobga olgan holda, aks holda u 0.099999 ... bo'ladi) va pastki qatorda - uchun DOUBLE PRECISION turi:

0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001

Shubhasiz, bu ikkita mutlaqo boshqa raqam! Shuning uchun, taqqoslashda birinchi raqam nol bilan to'ldiriladi va shuning uchun ikkinchisidan kattaroq bo'ladi (yaxlitlashni hisobga olgan holda - qalin bilan belgilangan). Bu bizning misollarimizdagi noaniqlikni tushuntiradi. Ikkinchi misolda aniq ko'rsatilgan 0.1 raqami DOUBLE PRECISION turiga o'tkaziladi va keyin REAL tipidagi raqamlar bilan taqqoslanadi. Ikkalasi ham bir xil turga qisqartirilgan va biz yuqorida ko'rgan narsaga egamiz. Keling, hamma narsa joyiga tushishi uchun so'rovni o'zgartiraylik:

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

Haqiqatan ham, 0.1 raqamini REAL va QO'SHAKTA ANQLIK ga ikki marta qisqartirish orqali biz topishmoqning javobini olamiz:

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

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

Bu yuqoridagi uchinchi misolni ham tushuntiradi. 123 456 789 raqami oddiy mantisani 24 bitga sig'dirish mumkin emas (23 aniq + 1 nazarda tutilgan). 24 bitga sig‘adigan maksimal butun son 224-1 = 16. Shuning uchun bizning 777 raqamimiz eng yaqin ifodalanadigan 215 gacha yaxlitlanadi. Turni DOUBLE PRECISION ga o‘zgartirib, biz endi bu stsenariyni ko‘rmaymiz:

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

Ana xolos. Ma'lum bo'lishicha, hech qanday mo''jiza yo'q. Ammo tasvirlangan hamma narsa sizga REAL turiga qanchalik muhtojligingiz haqida o'ylash uchun yaxshi sababdir. Ehtimol, uni qo'llashning eng katta afzalligi - ma'lum aniqlik yo'qolishi bilan hisob-kitoblarning tezligi. Ammo bu bunday tez-tez foydalanishni oqlaydigan universal stsenariy bo'ladimi? O'ylamang.

Manba: www.habr.com

a Izoh qo'shish