د ریښتیني ډولونو غیر ریښتیني ځانګړتیاوې، یا د REAL سره محتاط اوسئ

د خپریدو وروسته مقالې په PostgreSQL کې د ټایپ کولو ځانګړتیاو په اړه، لومړی نظر د اصلي شمیرو سره د کار کولو ستونزو په اړه و. ما پریکړه وکړه چې زما لپاره موجود د SQL پوښتنو کوډ ته یو ګړندی نظر واخلم ترڅو وګورم چې دوی څومره ځله د ریښتین ډول کاروي. دا معلومه شوه چې دا ډیری وختونه کارول کیږي، او پراختیا کونکي تل د هغې تر شا خطرونه نه پوهیږي. او دا د دې حقیقت سره سره چې د کمپیوټر حافظه کې د ریښتیني شمیرو ذخیره کولو ځانګړتیاو او د دوی سره کار کولو په اړه په انټرنیټ او هابری کې خورا ښه مقالې شتون لري. له همدې امله ، پدې مقاله کې به زه هڅه وکړم چې دا ډول ب featuresې په 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 строка)

برابر نه! څه معجزې! مګر نور، نور. یو څوک به ووایي، زه پوهیږم چې ریښتیا د جزونو سره بد چلند کوي، نو زه به هلته بشپړ شمیرې داخل کړم، او هرڅه به د دوی سره سم وي. ښه، راځئ چې ریښتیا ته 123 شمیره ورکړو:

********* ЗАПРОС *********
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 بایټس دي. او د ډبل PRECISION ډول لپاره، د مینټیسا اوږدوالی به 52 بټ وي، او ایکسپونټ به 11 بټ وي، د ټولټال 64 بټونو، یا 8 بایټ لپاره. PostgreSQL د فلوټینګ پوائنټ شمیرو لپاره د لوړ دقیقیت ملاتړ نه کوي.

راځئ چې زموږ د لسیزو شمیره 0.1 دواړه په ریښتیني او دوه اړخیز ډولونو کې بسته کړو. څرنګه چې د توجیه کونکي نښه او ارزښت یو شان دی، موږ به په مانټیسا تمرکز وکړو (زه په قصدي توګه د توضیحي ارزښتونو او صفر اصلي ارزښتونو ذخیره کولو غیر څرګند ځانګړتیاوې له مینځه وړم، ځکه چې دوی د ماهیت څخه پوهه پیچلې کوي او انحراف کوي. د ستونزې په اړه، که لیوالتیا وي، د IEEE 754 معیار وګورئ). موږ به څه ترلاسه کړو؟ په پورتنۍ کرښه کې به زه د ریښتیني ډول لپاره "مینټیسا" ورکړم (د 1 لخوا د وروستي بټ ګردي کولو په پام کې نیولو سره نږدې نمایشي شمیرې ته ، که نه نو دا به 0.099999 وي ...) ، او په لاندې کرښه کې - د دې لپاره د دوه ګونی دقیق ډول:

0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001

په ښکاره ډول دا دوه بشپړ مختلف شمیرې دي! له همدې امله، کله چې پرتله کول، لومړی نمبر به د صفر سره پیډ شي او له همدې امله به د دویم څخه لوی وي (د ګردي حساب په پام کې نیولو سره - هغه یو چې په بولډ نښه شوی). دا زموږ د مثالونو څخه ابهام تشریح کوي. په دویمه بیلګه کې، په واضح ډول ټاکل شوې شمیره 0.1 د ډبل دقیق ډول ته اچول کیږي، او بیا د یو شمیر ریال ډول سره پرتله کیږي. دواړه ورته ډول ته راټیټ شوي، او موږ هغه څه لرو چې موږ یې پورته ګورو. راځئ چې پوښتنه تعدیل کړو ترڅو هرڅه په خپل ځای کې راشي:

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

او په حقیقت کې ، د 0.1 شمیره دوه چنده کمولو سره ریښتیني او دوه چنده دقیقیت ته ، موږ د معما ځواب ترلاسه کوو:

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

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

دا پورته دریم مثال هم تشریح کوي. 123 شمیره ساده ده دا ناشونې ده چې مینټیسا په 24 بټونو کې فټ کړئ (۲۳ صریح + ۱ تقلید). اعظمي عدد چې په 23 بټونو کې فټ کیدی شي 1-24 = 224 دی. له همدې امله زموږ شمیره 1 نږدې نمایندګي 16 ته راټیټوي. د ډول په دوه ګوني دقیقیت بدلولو سره، موږ نور دا سناریو نه ګورو.

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

بس نور څه نه. دا معلومه شوه چې هیڅ معجزه شتون نلري. مګر هرڅه تشریح شوي یو ښه دلیل دی چې فکر وکړئ چې تاسو واقعیا څومره ریښتیا ډول ته اړتیا لرئ. شاید د دې کارولو لویه ګټه د محاسبې سرعت دی چې د دقت پیژندل شوي زیان سره. مګر ایا دا به یو نړیوال سناریو وي چې د دې ډول ډول مکرر کارول توجیه کړي؟ فکر مه کوه.

سرچینه: www.habr.com

Add a comment