Dopu a publicazione
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