Funzioni Unreal di Tipi Reali, o Attenti cù REAL

Dopu a publicazione articuli nantu à e caratteristiche di scrive in PostgreSQL, u primu cummentariu era nantu à e difficultà di travaglià cù numeri veri. Aghju decisu di piglià un ochju veloce à u codice di e dumande SQL dispunibuli per vede quantu spessu usanu u tipu REAL. Ci hè chì si usa abbastanza spessu, è i sviluppatori ùn capiscenu micca sempre i periculi daretu. E questu anche u fattu chì ci sò assai boni articuli in Internet è in Habré nantu à e caratteristiche di almacenà numeri veri in memoria di l'urdinatore è di travaglià cun elli. Dunque, in questu articulu, pruvaraghju à applicà tali funziunalità à PostgreSQL, è pruvate à piglià un ochju veloce à i prublemi assuciati cun elli, in modu chì serà più faciule per i sviluppatori di query SQL per evitarli.

A documentazione di PostgreSQL dice succintamente: "A gestione di tali errori è a so propagazione durante u calculu hè u sughjettu di una branca sana di matematica è informatica, è ùn hè micca coperta quì" (mentre si riferenu sapientemente u lettore à u standard IEEE 754). Chì tippu d'errori sò significati quì? Discutemu in ordine, è prestu diventerà chjaru perchè aghju pigliatu di novu a penna.

Pigliemu per esempiu una dumanda simplice:

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

In u risultatu, ùn vedemu nunda di spiciali - avemu da ottene u 0.1 previstu. Ma avà paragunemu à 0.1:

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

Ùn uguali ! Chì miraculi ! Ma in più, più. Qualchissia dicerà, sò chì REAL si cumporta male cù e fraccioni, cusì entre in numeri interi quì, è tuttu serà definitu bè cun elli. Ok, castemu u numeru 123 à REAL:

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

È hè diventatu 3 più! Eccu, a basa di dati hà finalmente scurdatu di cuntà ! O avemu sbagliatu qualcosa? Scupritemu.

Prima, ricurdemu di u materiale. Comu sapete, ogni numeru decimale pò esse allargatu in putenzi di deci. Dunque, u numeru 123.456 serà uguali à 1 * 102 + 2 * 101 + 3 * 100 + 4 * 10-1 + 5 * 10-2 + ​​​​6 * 10-3. Ma l'urdinatore opera cù numeri in forma binaria, per quessa, anu da esse rapprisintatu in forma di espansione in putenzi di dui. Dunque, u numeru 5.625 in binariu hè rapprisintatu cum'è 101.101 è serà uguali à 1*22 + 0*21 + 1*20 + 1*2-1 + 0*2-2 + 1*2-3. È s'è i putenzi pusitivi di dui dà sempre numeri decimali sanu (1, 2, 4, 8, 16, etc.), allora cù quelli negativi tuttu hè più cumplicatu (0.5, 0.25, 0.125, 0,0625, etc.). U prublema hè chì Ùn ogni decimale pò esse rapprisintatu cum'è una frazzioni binaria finita. Cusì, u nostru notu 0.1 in a forma di una frazzioni binaria appare cum'è u valore periculu 0.0 (0011). In cunseguenza, u valore finali di stu numeru in a memoria di l'urdinatore varierà secondu a prufundità di bit.

Avà hè u tempu di ricurdà cumu i numeri veri sò almacenati in a memoria di l'urdinatore. In generale, un numeru reale hè custituitu di trè parti principali - signu, mantissa è espunenti. U signu pò esse più o minus, cusì un bit hè attribuitu per questu. Ma u numaru di bits di a mantissa è l'esponente hè determinatu da u tipu reale. Allora, per u tipu REAL, a durata di a mantissa hè 23 bits (un bit uguale à 1 hè implicitamente aghjuntu à u principiu di a mantissa, è u risultatu hè 24), è l'esponente hè 8 bits. U totale hè 32 bit, o 4 byte. È per u tipu DOUBLE PRECISION, a durata di a mantissa serà 52 bits, è l'esponente serà 11 bits, per un totale di 64 bits, o 8 bytes. PostgreSQL ùn sustene micca una precisione più alta per i numeri in virgule flottante.

Imballemu u nostru numeru decimale 0.1 in i dui tipi REAL è DOUBLE PRECISION. Siccomu u signu è u valore di l'esponente sò listessi, ci focalizemu nantu à a mantissa (omette deliberatamente e caratteristiche micca evidenti di almacenà i valori di l'esponente è zero valori reali, postu chì complicanu l'intelligenza è distraenu da l'essenza. di u prublema, se interessatu, vede u standard IEEE 754). Chì averemu ? In a prima linea daraghju a "mantissa" per u tipu REAL (pigliandu in contu l'arrotondamentu di l'ultimu bit da 1 à u numeru rapprisentabile più vicinu, altrimenti serà 0.099999 ...), è in a linea di fondu - per u tipu DOUBLE PRECISION:

0.000110011001100110011001101
0.00011001100110011001100110011001100110011001100110011001

Ovviamente questi sò dui numeri completamente diffirenti! Dunque, quandu si paragunate, u primu numeru serà padded cù zeri è, per quessa, serà più grande di u sicondu (pigliandu in contu l'arrotondamentu - quellu marcatu in grassu). Questu spiega l'ambiguità da i nostri esempi. In u sicondu esempiu, u numeru esplicitamente specificatu 0.1 hè cast à u tipu DOUBLE PRECISION, è poi paragunatu cù un numeru di u tipu REAL. Tutti dui sò ridotti à u listessu tipu, è avemu esattamente ciò chì vedemu sopra. Mudificàmu a dumanda per chì tuttu cade in u locu:

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

È infatti, eseguendu una doppia riduzzione di u numeru 0.1 à REAL è DOUBLE PRECISION, avemu a risposta à l'enigma:

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

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

Questu spiega ancu u terzu esempiu sopra. U numeru 123 hè simplice hè impussibile di mette a mantissa in 24 bits (23 esplicitu + 1 implicitu). U massimu integer chì pò mette in 24 bits hè 224-1 = 16 777 215. Per quessa, u nostru numeru 123 456 789 hè arrotondatu à u rapprisintanti più vicinu 123 456 792. Cambiendu u tipu à DOUBLE PRECISION, ùn vedemu più stu scenariu:

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

Eccu tuttu. Risulta chì ùn ci hè micca miraculi. Ma tuttu ciò chì hè descrittu hè un bonu mutivu per pensà à quantu avete veramente bisognu di u tipu REAL. Forsi u vantaghju maiò di u so usu hè a vitezza di i calculi cù una perdita cunnisciuta di precisione. Ma serà questu un scenariu universale chì justificà l'usu frequente di stu tipu? Ùn pensate micca.

Source: www.habr.com

Add a comment