Fitur anu teu nyata tina jinis nyata, atanapi ati-ati sareng REAL

Sanggeus publikasi tulisan ngeunaan fitur ngetik dina PostgreSQL, koméntar anu pangheulana nyaéta ngeunaan kasusah gawé bareng wilangan riil. Kuring mutuskeun nyandak katingal gancang dina kode tina queries SQL sadia pikeun kuring ningali sabaraha sering aranjeunna ngagunakeun tipe REAL. Tétéla éta dipaké rada mindeng, sarta pamekar teu salawasna ngartos bahaya balik eta. Sareng ieu sanaos kanyataan yén aya seueur tulisan anu saé dina Internét sareng dina Habré ngeunaan fitur nyimpen wilangan riil dina mémori komputer sareng ngeunaan damel sareng aranjeunna. Ku alatan éta, dina artikel ieu kuring baris coba nerapkeun fitur sapertos ka PostgreSQL, sarta bakal coba nyandak katingal gancang dina troubles pakait sareng aranjeunna, meh bakal gampang pikeun pamekar query SQL ulah aranjeunna.

Dokuméntasi PostgreSQL nyatakeun sacara ringkes: "Manajemén kasalahan sapertos kitu sareng panyebaranna salami komputasi mangrupikeun subyek sadaya cabang matematika sareng élmu komputer, sareng henteu katutupan di dieu" (bari sacara bijaksana ngarujuk pamaca kana standar IEEE 754). Jenis kasalahan naon anu dimaksud di dieu? Hayu urang bahas aranjeunna dina urutan, sareng éta engké bakal écés naha kuring nyandak pulpén deui.

Hayu urang nyandak contona hiji pamundut basajan:

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

Hasilna, urang moal ningali nanaon husus - urang bakal meunang ekspektasi 0.1. Tapi ayeuna urang bandingkeun ka 0.1:

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

Teu sarua! Naon mujijat! Tapi salajengna, langkung. Batur bakal nyebutkeun, Kuring nyaho yén REAL behaves parah kalawan fraksi, jadi kuring gé asupkeun sakabeh angka aya, jeung sagalana pasti bakal rupa sareng maranehna. Ok, hayu urang tuang nomer 123 ka REAL:

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

Jeung tétéla 3 deui! Tuh, pangkalan data tungtungna hilap kumaha ngitung! Atawa urang salah harti hal? Hayu urang angka eta kaluar.

Kahiji, hayu urang inget materiel. Sakumaha anjeun terang, naon waé angka perpuluhan tiasa dilegakeun kana kakuatan sapuluh. Jadi, jumlah 123.456 bakal sarua jeung 1*102 + 2*101 + 3*100 + 4*10-1 + 5*10-2 + ​​6*10-3. Tapi komputer beroperasi kalawan angka dina formulir binér, ku kituna maranéhanana kudu digambarkeun dina bentuk ékspansi dina kakuatan dua. Ku alatan éta, angka 5.625 dina binér digambarkeun salaku 101.101 sarta bakal sarua jeung 1*22 + 0*21 + 1*20 + 1*2-1 + 0*2-2 + 1*2-3. Sareng upami kakuatan positip dua salawasna masihan nomer desimal (1, 2, 4, 8, 16, sareng sajabana), teras sareng anu négatip sadayana langkung rumit (0.5, 0.25, 0.125, 0,0625, jsb). Masalahna nyaéta éta Henteu unggal decimal bisa digambarkeun salaku fraksi binér terhingga. Ku kituna, urang notorious 0.1 dina bentuk fraksi binér muncul salaku nilai periodik 0.0(0011). Akibatna, nilai ahir angka ieu dina mémori komputer bakal rupa-rupa gumantung kana jero bit.

Ayeuna nya éta waktu pikeun nginget kumaha angka nyata disimpen dina mémori komputer. Sacara umum, wilangan riil diwangun ku tilu bagian utama - tanda, mantissa jeung éksponén. Tandana tiasa janten tambah atanapi dikurangan, janten hiji bit dialokasikeun pikeun éta. Tapi jumlah bit tina mantissa jeung éksponén ditangtukeun ku tipe nyata. Ku kituna, pikeun tipe REAL, panjang mantissa nyaeta 23 bit (hiji bit sarua jeung 1 implicitly ditambahkeun kana awal mantissa, sarta hasilna mangrupa 24), sarta éksponén nyaéta 8 bit. Jumlahna aya 32 bit, atanapi 4 bait. Sareng pikeun jinis DOUBLE PRECISION, panjang mantissa bakal 52 bit, sareng eksponenna 11 bit, jumlahna aya 64 bit, atanapi 8 bait. PostgreSQL teu ngarojong precision luhur pikeun angka floating titik.

Hayu urang bungkus angka desimal 0.1 kana jinis REAL sareng DOUBLE PRESISION. Kusabab tanda sareng nilai eksponen sami, urang bakal difokuskeun mantissa (kuring ngahaja ngaleungitkeun fitur anu teu jelas pikeun nyimpen nilai eksponen sareng nol nilai nyata, sabab ngahesekeun pamahaman sareng ngaganggu hakekatna. tina masalah, lamun kabetot, tingali standar IEEE 754). Naon anu bakal urang kéngingkeun? Dina garis luhur kuring bakal masihan "mantissa" pikeun tipe REAL (nyandak kana akun rounding tina bit panungtungan ku 1 ka angka representable pangcaketna, disebutkeun eta bakal 0.099999 ...), sarta dina garis handap - pikeun Tipe GANDA PRESISI:

0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001

Jelas ieu dua angka lengkep beda! Ku alatan éta, nalika ngabandingkeun, jumlah kahiji bakal empuk jeung nol, sarta, ku kituna, bakal leuwih gede ti kadua (nyandak kana rounding akun - hiji ditandaan di kandel). Ieu ngécéskeun ambiguitas tina conto urang. Dina conto anu kadua, angka 0.1 anu dijelaskeun sacara eksplisit dialungkeun kana jinis PRESISI GANDA, teras dibandingkeun sareng sajumlah jinis REAL. Duanana diréduksi jadi tipe sarua, sarta kami boga kahayang urang tingali di luhur. Hayu urang ngaropea pamundut supados sadayana janten tempatna:

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

Sareng leres, ku ngalaksanakeun pangurangan ganda tina angka 0.1 ka REAL sareng GANDA PRESISI, urang nampi jawaban kana tatarucingan:

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

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

Ieu ogé ngajelaskeun conto katilu di luhur. Jumlah 123 basajan mustahil pikeun nyocogkeun mantissa kana 24 bit (23 eksplisit + 1 tersirat). Integer maksimum nu bisa nyocogkeun kana 24 bit nyaeta 224-1 = 16. Ku alatan éta, angka urang 777 rounded ka representable pangcaketna 215. Ku ngarobah tipe kana DOUBLE PRESISION, urang moal deui ningali skenario kieu:

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

Éta hungkul. Tétéla teu aya mujijat. Tapi sadayana anu dijelaskeun mangrupikeun alesan anu saé pikeun mikirkeun sabaraha anjeun peryogi jinis REAL. Bisa oge kaunggulan pangbadagna ti pamakéan na nyaeta laju itungan jeung leungitna dipikawanoh tina akurasi. Tapi bakal ieu skenario universal anu bakal menerkeun pamakéan sering sapertos tipe ieu? Tong mikir.

sumber: www.habr.com

Tambahkeun komentar