Articles

pericolele utilizării tipurilor de date Float sau reale

există o glumă veche despre aritmetica în virgulă mobilă:

„Dacă tai un tort în trei, fiecare bucată este de 0,33 din tort. Dacă lipesc toate cele trei bucăți la loc, Asta îmi dă 0,99 din tort. Unde a dispărut restul tortului?”
— ” simplu. Asta e puțin blocat pe cuțit ”

aritmetica în virgulă mobilă este totul despre tolerarea și gestionarea aproximare pentru a evita erorile de preaplin în calcule. În lumea reală, de obicei ne pasă de precizie în număr și, în schimb, vom sacrifica spațiu și resurse pentru a evita revărsarea.în timp ce știința lucrează fericit într-o marjă de eroare, precizia contează în contabilitatea afacerilor. Când eram programator cub, am scris odată ceea ce credeam că este un mod perfect potrivit de a calcula profitul tranzacțiilor cu brokeri. Într-un milion de lire sterline, a fost un penny sau două afară cel mult. Am fost foarte mulțumit. A folosit calculele inerente compilatorului PL / 1 pe care l-am folosit la acea vreme pentru a dezvolta pachete financiare. Le-am arătat aplicația fin artizanale, și au fost îngroziți. Un bănuț într-un milion de lire sterline părea comercianților din orașul greu să fie nesăbuiți. Ei nu ar avea. Am fost forțat să scrie un pachet binar-codificate-zecimal (BCD) în codul de asamblare, care a fost exact exacte.

SQL Prompt are o regulă de analiză a codului (BP023) care vă va avertiza cu privire la utilizareaFLOAT sauREAL tipuri de date, datorită inexactităților semnificative pe care le pot introduce la tipul de calcule pe care multe organizații le vor efectua în mod obișnuit pe datele lor SQL Server.

tipuri de date cu număr aproximativ

aritmetica în virgulă mobilă a fost concepută într-un moment în care era o prioritate salvarea memoriei, oferind în același timp un mod versatil de a face calcule care implicau un număr mare. Deși este încă util pentru multe tipuri de calcule științifice, în special pentru cele care sunt conforme cu standardul IEEE 754 cu dublă precizie pentru aritmetica în virgulă mobilă, este, în mod necesar, un compromis. Indiciul este în numele acestui tip de date și aritmetică: ‘aproximativ’. Numerele în virgulă mobilă nu pot reprezenta cu exactitate toate numerele reale: în plus, operațiile în virgulă mobilă nu pot reprezenta cu exactitate toate operațiile aritmetice. Cu toate acestea, intervalul de mărime al numărului pe care îl pot deține este mult mai mare decât este posibil în alte tipuri numerice, chiar dacă nu este întotdeauna ținut cu exactitate.

problemele care apar din utilizarea calculelor în virgulă mobilă se datorează rotunjirii în timpul calculelor complexe și sunt cel mai adesea observate dacă datele sunt ‘condiționate’, astfel încât mici modificări ale intrărilor sunt mărite în ieșire. Inexactitățile sunt mult mai puțin evidente cu o precizie sporită a reprezentării numerelor, dar ele sunt încă prezente, totuși. Există, de asemenea, unele restricții ezoterice în utilizarea numerelor care sunt valide, dar nu pot fi reprezentate în virgulă mobilă, cum ar fi tan(2), dar acestea sunt susceptibile de a excita doar matematicieni.

SQL Server în virgulă mobilă tipuri de date

standardul SQL are trei virgulă mobilă, tipuri de date aproximative, REALDOUBLEPRECISION și FLOAT(n). SQL Server este conform cu aceasta, cu excepția faptului că nu are DOUBLEPRECISION tip de date, folosind FLOAT(53) în schimb. FLOAT(24) șiFLOAT(53) tipurile de date corespund Binary32 (Single) și Binary64 (double) în standardul IEEE 754 și sunt stocate în 4 și 8 octeți și 7 și 16 cifre deținute, în consecință. Ele sunt utile atunci când este important ca calculele să producă același rezultat ca o aplicație care utilizează.NET framework care utilizează și IEEE 754. Tipul de precizie dublă este, de asemenea, necesar atunci când numerele depășesc în magnitudinea lor maximul permis de DECIMAL tip de date (38 de cifre), deși cu pierdere de precizie. Numerele aproximative nu pot fi, desigur, utilizate în mod fiabil în niciun test de egalitate, cum ar fi o clauză WHERE.

calcule folosind tipul de date REAL (precizie unică)

voi încercaREAL tipul de date. FLOAT(24) tipul de date, sau mai mic, reacționează în același mod. Primul lucru de reținut atunci când experimentați cu numere în virgulă mobilă în SQL Server este că SSMS redă un număr în virgulă mobilă într-un mod care ascunde mici diferențe. De exemplu:

1
selectați Convert(REAL,0.100000001490116119384765625)

…dă 0,1

pentru a vedea mai exact ce valoare este stocată într-un număr în virgulă mobilă, trebuie să utilizați funcția str (), specificând precizia dorită de fapt.

1
2
3
4
5

/*sunt un pic nedrept aici, deoarece numărul zecimal 0.1 nu este reprezentabil
în virgulă mobilă; reprezentarea binară exactă ar avea o secvență „1100”
continuând la nesfârșit:*/
declarați @firstapproximate real = 0.1
selectați str(@firstapproximate,20,16)-ar trebui să fie 0.100000001490116119384765625

deja, acest lucru este alarmant. La urma urmei, avem de-a face cu date cu milioane de rânduri, astfel încât erorile mici se vor acumula, cu excepția cazului în care, cum ar fi rotunjirea bancherilor, se depășesc în medie. Această eroare este deja aproape de ‘penny într-un milion de lire sterline’ (1/ 240000000) pe care am menționat în introducere!

să evităm 0.1 și să-l punem la un ciudat de virgulă mobilă. Ce zici de împărțirea 1 la 3. Cu siguranță acest lucru nu ar putea fi o problemă?

1
2
3
4
5

declarând @payoffs REAL = 1
declarând @divisor real =3
selectați str(@payoffs /@divisor,20,16) ca coeficient
–produce 0.33333333432674408
–ar trebui să fie 0.3333333333333333333333

Oops. Am înțeles greșit. OK, este o mică eroare, dar amintiți-vă povestea mea despre bancheri. Un răspuns este fie corect, fie greșit, nu există nuanțe de gri pentru bărbații în costume gri. În școala de afaceri, există doar o bifă și o cruce. Nici un semn că înseamnă’destul de aproape’.

un test simplu este de a împărți unul cu numere de la unu la douăzeci. Ce ar putea merge prost?

putem stoca rezultatele calculului în virgulă mobilă și numerică, ambele convertite în șiruri și apoi comparăm șirurile (fiți avertizați căSTR() poate pune într-un spațiu de conducere care face o complicație).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

DECLARE @divisor REAL, @dividend REAL = 1
DECLARE @FloatingPointCalculations TABLE (Divisor INT, QuotientFloatingPoint VARCHAR(20), QuotientNumeric VARCHAR(20));
DECLARE @ii INT = 1;
DECLARE @iiMax INT = 20;
WHILE (@ii <= @iiMax)
BEGIN
SELECT @divisor = @ii;
INSERT INTO @FloatingPointCalculations (Divisor, QuotientFloatingPoint,
QuotientNumeric)
SELECT @ii AS divisor, Str(@Dividend / @divisor, 20, 16) AS QuotientFloatingPoint,
Convert(VARCHAR(20), 1.0000000 / @ii) AS QuotientNumeric;
SELECT @ii += 1;
END;
SELECT The.Divisor, The.QuotientFloatingPoint, The.QuotientNumeric
FROM @FloatingPointCalculations AS The;

Now, what if we list the rows where the numbers don’t match?

1
2
3

SELECT The.Divisor, The.QuotientFloatingPoint, The.QuotientNumeric
FROM @FloatingPointCalculations The
WHERE Left(LTrim(The.QuotientFloatingPoint),16)<> Left(LTrim(The.QuotientNumeric), 16)

Ugh! Doar acolo unde divizorul Era 1, 2, 4, 8 sau 16 a existat un rezultat corect.

în cazul în care sunteți în speranța că într-un fel float a fost corectă, iar versiunea numerică nu a fost, aici este coeficientul numeric calculat în Excel:

calcule folosind FLOAT(25) sau peste (dublu precizie)

dacă utilizați dublu precizie virgulă mobilă, FLOAT(25) sau peste, testele sunt toate trecut, din cauza STR() funcția permite un maxim de șaisprezece locuri în dreapta punctului zecimal. Dacă există mai mult de 16, atunci rezultatul este trunchiat. Tipul de date cu precizie dublă are șaisprezece cifre, în timp ce tipul de date cu precizie unică are șapte. De asemenea, veți fi văzut că tipul de date de precizie unic primește primele șapte cifre corecte. De asemenea, dubla precizie primește primele șaisprezece cifre corecte. Putem extinde doar numărul pentru a vedea aproximarea.

1
2

declarați @Firstaproximate FLOAT(53) = 100000000000000000000.1
selectați str(@firstapproximate,40,16) ca bignumberwithadecimal

acea parte fracționată a dispărut, nu-i așa? Este, probabil, doar o mică diferență, dar în unele calcule, aceasta poate provoca probleme.

concluzie

aritmetica în virgulă mobilă este rapidă și economică pe stocare, dar oferă un rezultat aproximativ. Este potrivit pentru aplicații științifice bine condiționate, dar nu și pentru calcule financiare, ceea ce cere ca un număr să fie ‘corect’ sau ‘greșit’. De asemenea, are dezavantajul suplimentar într-o bază de date, deoarece nu puteți testa în mod fiabil și consecvent două numere aproximative pentru egalitate.

nu este corect să spunem că nu ar trebui să utilizați niciodată numere în virgulă mobilă în tipurile de date SQL sau în aritmetică. Tipuri aproximative sunt acolo în standardul SQL pentru un scop. În zilele noastre, aș rămâne mereu cu tipul de date cu virgulă mobilă cu dublă precizie în SQL Server, unde există o cerință adecvată. Acestea sunt excelente în scopuri precum modelarea sistemelor meteorologice sau trasarea traiectoriilor, dar nu și pentru tipurile de calcule pentru care organizația medie este probabil să utilizeze o bază de date.

Dacă observați utilizarea eronată a acestor tipuri, atunci ar trebui să treceți la un tipDECIMALNUMERIC adecvat. Dacă știți că aveți nevoie de aritmetică în virgulă mobilă și puteți explica de ce, atunci probabil știți suficient pentru a evita capcanele punctului plutitor, cum ar fi cea care a avut loc în celebrul eșec al rachetelor Patriot care a dus direct la 28 de decese.