Articles

Nebezpečí použití Float nebo Real datové typy

Tam je starý vtip o plovoucí-bod, aritmetika:

„kdybych řez dort ve třech, každý kus je 0.33 dortu. Když přilepím všechny tři kousky zpět k sobě, dá mi to 0,99 dortu. Kam zmizel zbytek dortu?“
– “ Jednoduché. To je trochu přilepená na nůž“

Plovoucí-bod, aritmetika je o tolerování a řízení sbližování s cílem, aby se zabránilo přetečení chyby ve výpočtech. V reálném světě se obvykle staráme o přesnost v číslech a místo toho obětujeme prostor a zdroje, abychom se vyhnuli přetečení.

zatímco věda pracuje šťastně v mezích chyby, přesnost je důležitá v obchodním účetnictví. Když jsem byla mládě, programátor, jednou jsem napsal co jsem si myslel, že je dokonale vhodný způsob výpočtu zisku makléře nabídky. V milion liber, to bylo penny nebo dva ven nanejvýš. Měl jsem velkou radost. Použil výpočty obsažené v kompilátoru PL / 1, které jsme v té době použili k vývoji finančních balíčků. Ukázal jsem jim jemně vytvořenou aplikaci a oni byli zděšeni. Penny v milionu liber se zdálo natvrdo město obchodníků, být bezohledný. Neměli by to. Byl jsem nucen napsat binárně kódovaný desetinný (BCD) balíček v assembleru, který byl přesně přesný.

SQL Řádek má kód, analýzu pravidlo (BP023), které vás upozorní na používání FLOAT nebo REAL datové typy, vzhledem k značné nepřesnosti mohou zavést do jakési výpočty, že mnoho organizací se bude běžně provádět na vlastní SQL Server data.

Orientační-číslo datové typy

Plovoucí-bod, aritmetika byl vymyšlen v době, kdy bylo prioritou ušetřit paměť, přičemž univerzální způsob, jak dělat výpočty, které se podílí velké množství. Ačkoli je stále užitečný pro mnoho typů vědeckých výpočtů, zejména těch, které odpovídají standardu IEEE 754 s dvojitou přesností pro aritmetiku s plovoucí desetinnou čárkou, je to nezbytně kompromis. Vodítko je v názvu tohoto typu dat a aritmetiky: „přibližné“. Čísla s plovoucí desetinnou čárkou nemohou přesně reprezentovat všechna reálná čísla: operace s plovoucí desetinnou čárkou navíc nemohou přesně reprezentovat všechny aritmetické operace. Rozsah velikosti čísla, které mohou držet, je však mnohem větší, než je možné u jiných číselných typů, i když to není vždy přesně drženo.

problémy, které vznikají z používání floating-point výpočtů jsou vzhledem k round-off v průběhu složitých výpočtů, a jsou nejčastěji vidět, když data je špatně podmíněné‘, tak, že malé změny v zadání jsou zvětšeny ve výstupu. Nepřesnosti jsou mnohem méně patrné se zvýšenou přesností reprezentace čísel, ale stále jsou přítomny, nicméně. Existují také některá Esoterická omezení v používání čísel, která jsou platná, ale nemohou být reprezentována v plovoucí desetinné čárce, jako je tan (π / 2), ale pravděpodobně vzrušují pouze matematiky.

SQL Server plovoucí bodu datové typy

SQL Standard má tři plovoucí desetinnou čárkou, přibližné datové typy, REALDOUBLEPRECISIONFLOAT(n). SQL Server odpovídá na to až na to, že nemá DOUBLEPRECISION datový typ, pomocí FLOAT(53) místo. FLOAT(24)FLOAT(53) datatypes odpovídá Binary32 (Single) a Binary64 (double) v IEEE 754 standard, a jsou uloženy ve 4 a 8 bajtů, a 7 a 16 číslic konat, odpovídajícím způsobem. Jsou užitečné, když je důležité, aby výpočty přinesly stejný výsledek jako aplikace používající. NET framework, která také používá IEEE 754. Double precision typ je také zapotřebí, když čísla vyšší než v jejich velikosti maximální povolené DECIMAL datatype (38 číslic), i když se ztrátou přesnosti. Přibližná čísla samozřejmě nelze spolehlivě použít v žádném testu rovnosti, jako je klauzule WHERE.

výpočty pomocí reálného datového typu (single precision)

zkusím REAL datový typ. FLOAT(24) datový typ, nebo menší, reaguje stejným způsobem. První věc, kterou nezapomeňte při experimentování s plovoucí bod čísla v SQL Serveru je to SSMS činí desetinné číslo tak, že převleky malé rozdíly. Například:

1
VYBERTE možnost Převést(REAL,0.100000001490116119384765625)

…dává 0,1

vidět přesněji, co je uložena hodnota v plovoucí bod číslo, budete muset použít funkci STR (), s uvedením přesné vlastně chcete.

1
2
3
4
5

/*jsem trochu nefér tady, protože desetinné číslo 0,1 není reprezentovatelná
v plovoucí desetinnou čárkou; přesné binární reprezentace by „1100“ sekvence
pokračovat donekonečna:*/
DECLARE @FirstApproximate REAL = 0.1
ZVOLTE Str(@FirstApproximate,20,16) –by měla být 0.100000001490116119384765625

Už je to alarmující. Jsme, koneckonců, nakládání s daty, s miliony řádků, takže drobné chyby budou hromadit, pokud, jako bankéři zaokrouhlení‘, které v průměru. Tato chyba je již blízko penny za milion liber‘ (1/ 240000000), které jsem zmínil v úvodu!

vyhýbejme se 0,1 a snižme to na šílenost s plovoucí desetinnou čárkou. Co takhle dělení 1 na 3. Určitě to nemůže být problém?

1
2
3
4
5

s UVEDENÍM @výplaty REAL = 1
s UVEDENÍM @dělitel REAL =3
ZVOLTE Str(@výplaty /@dělitel,20,16) jako kvocient
– vytváří 0.3333333432674408
… mělo by to být 0.3333333333333333

Omlouváme se. Je to špatně. OK, je to malá chyba, ale pamatujte si můj příběh o bankéřích. Odpověď je buď správná, nebo špatná, pro muže v šedých oblecích neexistují žádné odstíny šedé. V obchodní škole je jen klíště a kříž. Žádné znamení, které znamená „dost blízko“.

jednoduchý test je rozdělit jeden podle čísel od jednoho do dvaceti. Co by se mohlo pokazit?

můžeme ukládat výsledky plovoucí desetinnou čárkou a numerický výpočet, oba převedeny na řetězce a pak jsme porovnat řetězce (být varováni, že STR() můžete dát v přední prostor, který je pro komplikace).

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.

Pouze tam, kde byl dělitel 1, 2, 4, 8 nebo 16, byl správný výsledek.

V případě, že doufáte, že float byl nějak přesný a numerická verze nebyla, zde je číselný kvocient vypočítaný v aplikaci Excel:

Výpočty pomocí FLOAT(25) nebo nad (dvojitá přesnost)

Pokud používáte dvojitou přesností plovoucí desetinnou čárkou, FLOAT(25) nebo nad, testy jsou všechny prošel, protože STR() funkce umožňuje maximálně šestnáct míst vpravo od desetinné čárky. Pokud je více než 16, výsledek je zkrácen. Dvojitý přesný datový typ má šestnáct číslic, zatímco jediný přesný datový typ má sedm. Také jste viděli, že jediný přesný datový typ dostane prvních sedm číslic správně. Stejně tak dvojnásobná přesnost dostane prvních šestnáct číslic správně. Můžeme jen rozšířit číslo, abychom viděli aproximaci.

1
2

DECLARE @FirstApproximate FLOAT(53) = 10000000000000000.1
ZVOLTE Str(@FirstApproximate,40,16) JAKO BigNumberWithaDecimal

zlomková část zmizela, ne? Je to pravděpodobně jen malý rozdíl, ale v některých výpočtech to může způsobit problémy.

závěr

aritmetika s plovoucí desetinnou čárkou je rychlá a ekonomická při skladování, ale poskytuje přibližný výsledek. Je vhodný pro dobře podmíněné vědecké aplikace, ale ne pro finanční výpočty, které vyžadují, aby číslo bylo buď „správné“ nebo „špatné“. Má také další nevýhodu v databázi, protože nemůžete spolehlivě a důsledně testovat dvě přibližná čísla pro rovnost.

není správné říkat, že byste nikdy neměli používat čísla s plovoucí desetinnou čárkou v datových typech SQL nebo v aritmetice. Přibližné typy jsou ve standardu SQL pro určitý účel. V dnešní době bych se vždy držel datového typu s dvojitou přesností s plovoucí desetinnou čárkou v SQL Serveru, kde je vhodný požadavek. Jsou skvělé pro takové účely, jako je modelování meteorologických systémů nebo Vykreslování trajektorií, ale ne pro typy výpočtů, pro které průměrná organizace pravděpodobně použije databázi.

Pokud zjistíte chybné použití těchto typů, měli byste místo toho přepnout na vhodný typ DECIMALNUMERIC. Pokud víte, že potřebujete plovoucí řádovou čárkou a může vysvětlit, proč, pak asi víte dost, aby se zabránilo nástrahy plovoucí bod, jako ten, který odehrál ve slavné Patriot missile selhání, které vedlo přímo do 28 úmrtí.