Articles

az úszó vagy a valódi adattípusok használatának veszélyei

van egy régi vicc a lebegőpontos aritmetikáról:

“ha háromra vágok egy tortát, minden darab 0, 33 a tortából. Ha mind a három darabot összeragasztom, az 0.99-et ad nekem a tortából. Hová tűnt a torta többi része?”
– ” egyszerű. Ez a kicsit beragadt a kés ”

lebegőpontos számtani szól toleráló és irányító közelítés elkerülése érdekében túlcsordulás hibák számítások. A való világban általában a számok precizitásával foglalkozunk, ehelyett a teret és az erőforrásokat áldozzuk fel a túlcsordulás elkerülése érdekében.

míg a tudomány boldogan működik hibahatáron belül, precíziós ügyek az üzleti könyvelésben. Amikor cub programozó voltam, egyszer írtam azt, amit tökéletesen alkalmasnak tartottam a tőzsdei ügyletek nyereségének kiszámítására. Egy millió font, ez volt egy-két fillért ki a legfeljebb. Elégedett voltam. A PL/1 fordítóban rejlő számításokat használta, amelyeket akkoriban pénzügyi csomagok kidolgozására használtunk. Megmutattam nekik a finoman kidolgozott alkalmazást, és elborzadtak. Egy fillért ki egy millió font úgy tűnt,hogy a hardboiled város kereskedők, hogy vakmerő. Nem lett volna meg. Kénytelen voltam írni egy bináris kódolt decimális (BCD) csomagot az assembler kódban, amely pontosan pontos volt.

az SQL Prompt rendelkezik egy kódelemzési szabály (BP023), amely figyelmezteti Önt a FLOAT vagy REAL datatypes használatára, mivel jelentős pontatlanságokat vezethetnek be olyan számításokhoz, amelyeket sok szervezet rutinszerűen végez az SQL Server adatain.

hozzávetőleges szám adattípusok

lebegőpontos számtani dolgozták ki, amikor ez volt a prioritás, hogy mentse a memóriát, miközben egy sokoldalú módja a számítások, hogy részt vett a nagy számok. Bár ez még mindig hasznos sokféle tudományos számítások, különösen azok, amelyek megfelelnek a kettős pontosságú IEEE 754 szabvány lebegőpontos számtani, ez, szükségszerűség, kompromisszum. A nyom az ilyen típusú adatok és számtani adatok nevében van: “hozzávetőleges”. A lebegőpontos számok nem tudják pontosan reprezentálni az összes valós számot: ezenkívül a lebegőpontos műveletek nem tudják pontosan reprezentálni az összes aritmetikai műveletet. Azonban a tartható szám nagyságrendje sokkal nagyobb, mint más numerikus típusokban lehetséges, még akkor is, ha nem mindig pontosan tartják.

A problémákat, amelyek abból erednek, hogy használja a lebegőpontos számítások miatt kör-le során bonyolult számításokat, valamint gyakran láthatók, ha az adatok ‘beteg-vel’, hogy kis változások bemeneti felnagyítva a kimenet. A pontatlanságok sokkal kevésbé nyilvánvalóak a számok ábrázolásának fokozott pontosságával, de ennek ellenére még mindig jelen vannak. Vannak olyan ezoterikus korlátozások is a számok használatában, amelyek érvényesek, de nem ábrázolhatók lebegőpontban, például tan(π/2), de ezek valószínűleg csak a matematikusokat izgatják.

SQL Server lebegőpontos adattípusok

az SQL szabványnak három lebegőpontos, hozzávetőleges adattípusa van, REALDOUBLEPRECISION és FLOAT(n). Az SQL Server megfelel ennek, kivéve, ha nincs DOUBLEPRECISION adattípus, a FLOAT(53) helyett. A FLOAT(24) és FLOAT(53) adattípusok megfelelnek az IEEE 754 szabvány Binary32 (Single) és Binary64 (double) típusainak, és 4 és 8 bájtban vannak tárolva, és ennek megfelelően 7 és 16 számjegyet tartanak. Ezek akkor hasznosak, ha fontos, hogy a számítások ugyanazt az eredményt hozzák, mint az IEEE 754-et is használó. Net keretrendszert használó alkalmazás. A kettős pontosságú típusra akkor is szükség van, ha a számok nagyságukban meghaladják a DECIMAL datatype (38 számjegy) által megengedett maximális értéket, bár precíziós veszteséggel. A hozzávetőleges számokat természetesen nem lehet megbízhatóan használni az egyenlőség bármely tesztjében, például egy WHERE záradék.

számítások a valós adattípus (single precision)

megpróbálom ki a REAL adattípus. A FLOAT(24) adattípus vagy kisebb, ugyanúgy reagál. Az első dolog, hogy emlékezzen, amikor kísérletezik lebegőpontos számok SQL Server, hogy SSMS teszi a lebegőpontos szám oly módon, hogy álcázza a kis különbségek. Például:

1
VÁLASSZA a Konvertálás(VALÓDI,0.100000001490116119384765625)

…ad 0.1

látni még pontosan, hogy milyen értéket tárolja a lebegőpontos szám, akkor használja az STR() függvény, amely meghatározza a pontosságot akarsz.

1
2
3
4
5

/*én egy kicsit igazságtalan itt, mert a decimális szám 0.1 nem ábrázolható
a lebegőpontos; a pontos bináris ábrázolása volna egy “1100” sorozat
folytatódik a végtelenségig:*/
KIJELENTEM @FirstApproximate VALÓDI = 0.1
VÁLASSZA ki a Str(@FirstApproximate,20,16) –legyen 0.100000001490116119384765625

Ez már riasztó. Végül is több millió sorral foglalkozunk az adatokkal, így kisebb hibák halmozódnak fel, hacsak, mint például a “bankárok kerekítése”, nem átlagolják őket. Ez a hiba már közel áll a “penny egy millió fontban” (1/ 240000000), amelyet a bevezetésben említettem!

kerüljük el a 0,1-et, és tegyük le a lebegőpontos torzszülöttre. Mi lenne, ha 1-et osztanánk 3-mal? Biztos, hogy ez nem lehet probléma?

1
2
3
4
5

FELTÜNTETVE @nyeremények VALÓDI = 1
FELTÜNTETVE @osztó, VALÓDI =3
VÁLASSZA ki a Str(@nyeremények /@osztó,20,16), mint hányados
–termel 0.3333333432674408
–kell 0.3333333333333333

Hoppá. Félreértette. Oké, ez egy apró hiba, de emlékezz a bankárokról szóló történetemre. A válasz vagy helyes, vagy rossz, nincsenek szürke árnyalatok a szürke öltönyös férfiak számára. Az üzleti iskolában csak egy kullancs és egy kereszt van. Nincs olyan jel, ami azt jelenti, hogy “elég közel”.

egy egyszerű teszt az, hogy egy számot osztunk egyről húszra. Mi baj lehet?

tárolhatjuk a lebegőpontos és numerikus számítás eredményeit, mind karakterláncokká alakítva, majd összehasonlítjuk a karakterláncokat (figyelmeztetni kell, hogy a STR() egy olyan vezető helyet helyezhet el, amely komplikációt okoz).

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)

Uh! Csak akkor volt helyes eredmény, ha az osztó 1, 2, 4, 8 vagy 16 volt.

abban az esetben, ha azt reméli, hogy valahogy az úszó pontos volt, és a numerikus verzió nem volt, itt van az Excelben kiszámított numerikus hányados:

számítások úszó(25) vagy több (kettős pontosság)

ha kettős pontosságú lebegőpontot használ, FLOAT(25) vagy több, a tesztek mind elhaladtak, mert a STR() funkció lehetővé teszi a maximális tizenhat hely a tizedes pont jobb oldalán. Ha több mint 16 van, akkor az eredmény csonka. A kettős pontosságú adattípus tizenhat számjegyből áll, míg az egyetlen pontossági adattípus hét. Azt is látta,hogy az egyetlen precíziós adattípus megkapja az első hét számjegyet. Hasonlóképpen, a kettős pontosságú lesz az első tizenhat számjegy jobb. Mi csak szélesíteni a számot, hogy a közelítés.

1
2

KIJELENTEM @FirstApproximate FLOAT(53) = 10000000000000000.1
VÁLASSZA ki a Str(@FirstApproximate,40,16) MINT BigNumberWithaDecimal

Ez tört része eltűnt, nem? Ez valószínűleg csak egy apró különbség, de egyes számításokban problémákat okozhat.

következtetés

a lebegőpontos aritmetika gyors és gazdaságos a tárolás során, de közelítő eredményt ad. Alkalmas jól kondicionált tudományos alkalmazásokra, de nem pénzügyi számításokra, amelyek azt igénylik, hogy egy szám “helyes” vagy “rossz”. Ez is az extra hátránya az adatbázisban, mert nem lehet megbízhatóan és következetesen tesztelni két hozzávetőleges számok az egyenlőség.

nem helyes azt mondani, hogy soha ne használjon lebegőpontos számokat SQL adattípusokban vagy aritmetikában. Hozzávetőleges típusok vannak az SQL szabványban egy célra. Én manapság mindig ragaszkodni a kettős pontosságú lebegőpontos adattípus SQL Server, ahol van egy megfelelő követelmény. Nagyszerűek olyan célokra, mint az időjárási rendszerek modellezése vagy a pályák ábrázolása, de nem azokra a számításokra, amelyekre az átlagos szervezet valószínűleg adatbázist használ.

Ha az ilyen típusok hibás használatát észleli, akkor át kell váltania egy megfelelő DECIMALNUMERIC típus helyett. Ha tudod, hogy szükség van lebegőpontos számtani és meg tudja magyarázni, hogy miért, akkor valószínűleg tudja elég ahhoz, hogy elkerüljék a buktatókat lebegőpontos, mint például az egyik, hogy történt a híres Patriot rakéta hiba vezetett közvetlenül 28 haláleset.