Real növlərin qeyri-real xüsusiyyətləri və ya REAL ilə diqqətli olun

Dərc edildikdən sonra Məqalə PostgreSQL-də yazmağın xüsusiyyətləri haqqında ilk şərh real rəqəmlərlə işləməyin çətinlikləri haqqında oldu. REAL növündən nə qədər tez-tez istifadə etdiklərini görmək üçün mənim üçün mövcud olan SQL sorğularının koduna tez nəzər salmaq qərarına gəldim. Məlum olub ki, o, kifayət qədər tez-tez istifadə olunur və tərtibatçılar heç də həmişə bunun arxasında duran təhlükələri başa düşmürlər. Bu, İnternetdə və Habré-də kompüter yaddaşında həqiqi nömrələrin saxlanmasının xüsusiyyətləri və onlarla işləmək haqqında kifayət qədər yaxşı məqalələrin olmasına baxmayaraq. Buna görə də, bu məqalədə bu cür xüsusiyyətləri PostgreSQL-ə tətbiq etməyə çalışacağam və SQL sorğu tərtibatçılarının onlardan qaçmağı asanlaşdırmaq üçün onlarla əlaqəli çətinliklərə tez nəzər salmağa çalışacağam.

PostgreSQL sənədlərində qısa şəkildə deyilir: “Belə xətaların idarə edilməsi və hesablama zamanı onların yayılması riyaziyyat və informatikanın bütöv bir sahəsinin mövzusudur və burada əhatə olunmur” (oxucunu IEEE 754 standartına ağıllı şəkildə istinad etməklə). Burada hansı səhvlər nəzərdə tutulur? Gəlin onları ardıcıllıqla müzakirə edək və tezliklə əlimə niyə yenidən qələm götürdüyüm aydınlaşacaq.

Məsələn, sadə bir sorğu götürək:

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

Nəticədə, biz xüsusi bir şey görməyəcəyik - gözlənilən 0.1-i alacağıq. Ancaq indi onu 0.1 ilə müqayisə edək:

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

Bərabər deyil! Nə möcüzələr! Ancaq daha çox, daha çox. Biri deyəcək ki, mən bilirəm ki, REAL kəsrlərlə pis davranır, ona görə də ora tam ədədləri daxil edəcəyəm və onlarla mütləq hər şey yaxşı olacaq. Yaxşı, gəlin REAL-a 123 rəqəmini verək:

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

Və daha 3 oldu! Budur, verilənlər bazası nəhayət saymağı unutdu! Yoxsa nəyisə səhv başa düşürük? Gəlin bunu anlayaq.

Əvvəlcə materialı xatırlayaq. Bildiyiniz kimi, istənilən onluq ədədi onluğun dərəcələrinə qədər genişləndirmək olar. Beləliklə, 123.456 rəqəmi 1*102 + 2*101 + 3*100 + 4*10-1 + 5*10-2 + ​​6*10-3-ə bərabər olacaq. Lakin kompüter ikilik formada ədədlərlə işləyir, ona görə də onlar ikinin gücündə genişlənmə şəklində təqdim edilməlidir. Buna görə də, binar sistemdə 5.625 rəqəmi 101.101 kimi təmsil olunur və 1*22 + 0*21 + 1*20 + 1*2-1 + 0*2-2 + 1*2-3-ə bərabər olacaqdır. İkinin müsbət səlahiyyətləri həmişə tam onluq ədədlər verirsə (1, 2, 4, 8, 16 və s.), onda mənfi olanlarla hər şey daha mürəkkəbdir (0.5, 0.25, 0.125, 0,0625 və s.). Problem ondadır Hər onluq sonlu ikilik kəsr kimi göstərilə bilməz. Beləliklə, ikili kəsr şəklində olan bədnam 0.1-imiz dövri qiymət 0.0(0011) kimi görünür. Nəticə etibarilə, bu ədədin kompüter yaddaşında son dəyəri bit dərinliyindən asılı olaraq dəyişəcək.

İndi real nömrələrin kompüter yaddaşında necə saxlandığını xatırlamağın vaxtıdır. Ümumiyyətlə, həqiqi ədəd üç əsas hissədən - işarə, mantis və eksponentdən ibarətdir. İşarə həm artı, həm də mənfi ola bilər, ona görə də onun üçün bir bit ayrılır. Lakin mantisanın və eksponentin bitlərinin sayı real tipə görə müəyyən edilir. Deməli, REAL növü üçün mantisanın uzunluğu 23 bitdir (1-ə bərabər bir bit mantisanın əvvəlinə dolayısı ilə əlavə olunur və nəticə 24-dür), eksponent isə 8 bitdir. Cəmi 32 bit və ya 4 baytdır. DOUBLE PRECISION növü üçün isə mantisanın uzunluğu 52 bit, eksponent isə 11 bit, cəmi 64 bit və ya 8 bayt olacaq. PostgreSQL üzən nöqtə nömrələri üçün daha yüksək dəqiqliyi dəstəkləmir.

Gəlin 0.1 onluq ədədimizi həm REAL, həm də CİFƏL DƏZİNLİK növlərinə yığaq. Eksponentin işarəsi və dəyəri eyni olduğundan, diqqətimizi mantisaya yönəldəcəyik (Mən eksponentin dəyərlərini saxlamağın qeyri-aşkar xüsusiyyətlərini və sıfır real dəyərləri qəsdən buraxıram, çünki onlar başa düşməyi çətinləşdirir və mahiyyətdən yayındırır. Əgər maraqlanırsınızsa, IEEE 754 standartına baxın). Nə alacağıq? Üst sətirdə REAL növü üçün “mantissa” verəcəyəm (son bitin ən yaxın təmsil olunan rəqəmə 1-ə yuvarlaqlaşdırılması nəzərə alınmaqla, əks halda 0.099999 olacaq...), alt sətirdə isə - üçün. DOUBLE DƏqiqlik növü:

0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001

Aydındır ki, bunlar tamamilə fərqli iki rəqəmdir! Buna görə də, müqayisə edərkən birinci nömrə sıfırlarla doldurulacaq və buna görə də ikincidən daha böyük olacaqdır (yuvarlaqlaşdırma nəzərə alınmaqla - qalın hərflə qeyd olunan). Bu, misallarımızdakı qeyri-müəyyənliyi izah edir. İkinci misalda, açıq şəkildə göstərilən 0.1 rəqəmi DOUBLE RECISION növünə ötürülür və sonra REAL tipli bir sıra ilə müqayisə edilir. Hər ikisi eyni tipə endirilib və yuxarıda gördüyümüz tam olaraq bizdə var. Gəlin sorğunu elə dəyişdirək ki, hər şey öz yerinə düşsün:

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

Və həqiqətən də, 0.1 rəqəmini REAL və CİFƏL DƏqiqliyə ikiqat azaltmaqla biz tapmacanın cavabını alırıq:

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

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

Bu da yuxarıdakı üçüncü nümunəni izah edir. 123 rəqəmi sadədir mantisanı 24 bitə sığdırmaq mümkün deyil (23 açıq + 1 nəzərdə tutulur). 24 bitə sığa bilən maksimum tam ədəd 224-1 = 16-dir. Buna görə də, 777 rəqəmimiz ən yaxın təmsil olunan 215-ə yuvarlaqlaşdırılıb. Növü DOUBLE RECISION olaraq dəyişdirməklə, biz artıq bu ssenarini görmürük:

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

Hamısı budur. Belə çıxır ki, heç bir möcüzə yoxdur. Ancaq təsvir olunan hər şey REAL tipə nə qədər ehtiyacınız olduğunu düşünmək üçün yaxşı bir səbəbdir. Bəlkə də istifadəsinin ən böyük üstünlüyü məlum dəqiqlik itkisi ilə hesablamaların sürətidir. Bəs bu, bu cür tez-tez istifadəyə haqq qazandıran universal bir ssenari ola bilərmi? Düşünmə.

Mənbə: www.habr.com

Добавить комментарий