Articles

de gevaren van het gebruik van Float of echte Datatypes

Er is een oude grap over floating-point rekenkunde:

” als ik een taart in drie snijd, is elk stuk 0,33 van de taart. Als ik alle drie de stukken weer aan elkaar plak, krijg ik 0,99 van de taart. Waar is de rest van de taart gebleven?”
— ” Eenvoudig. Dat is het kleine beetje geplakt op het mes”

Floating-point rekenkunde is alles over het tolereren en beheren van benadering om overflow fouten in berekeningen te voorkomen. In de echte wereld geven we meestal om precisie in aantallen en zullen we in plaats daarvan ruimte en middelen opofferen om overloop te voorkomen.terwijl wetenschap gelukkig werkt binnen een foutenmarge, is precisie belangrijk in de bedrijfsboekhouding. Toen ik een cub programmeur was, schreef ik ooit wat ik dacht dat een perfect geschikte manier was om de winst van effectenmakelaar deals te berekenen. In een miljoen pond was het hooguit een cent of twee. Ik was blij. Het gebruikte de berekeningen die inherent zijn aan de PL/1 compiler die we destijds gebruikten om financiële pakketten te ontwikkelen. Ik liet ze de fijn gemaakte applicatie zien, en ze waren geschokt. Een cent in een miljoen pond leek de hardboiled stad handelaren roekeloos te zijn. Ze wilden het niet hebben. Ik werd gedwongen om een binair-gecodeerd-decimaal (BCD) pakket te schrijven in assembler code die precies accuraat was.

SQL Prompt heeft een code analyseregel (BP023) die u waarschuwt voor het gebruik van FLOAT of REAL datatypes, vanwege de belangrijke onnauwkeurigheden die ze kunnen introduceren in het soort berekeningen dat veel organisaties routinematig zullen uitvoeren op hun SQL Server data.

geschatte getallengegevenstypen

Floating-point arithmetics werd bedacht op een moment dat het een prioriteit was om geheugen te besparen terwijl het een veelzijdige manier was om berekeningen te doen waarbij grote getallen betrokken waren. Hoewel het nog steeds nuttig is voor vele soorten wetenschappelijke berekeningen, met name die welke voldoen aan de dubbele precisie IEEE 754-norm voor floating point rekenkunde, is het noodzakelijkerwijs een compromis. De aanwijzing is in de naam van dit type gegevens en rekenkunde: ‘bij benadering’. Zwevende-kommagetallen kunnen niet nauwkeurig alle reële getallen weergeven: bovendien kunnen zwevende-kommabewerkingen niet nauwkeurig alle rekenkundige bewerkingen weergeven. Echter, het bereik van de grootte van het getal dat ze kunnen houden is veel groter dan mogelijk is in andere numerieke types, zelfs als het niet altijd nauwkeurig wordt gehouden.

de problemen die voortvloeien uit het gebruik van floating-point berekeningen zijn te wijten aan afronding tijdens complexe berekeningen, en worden meestal gezien als de gegevens “slecht geconditioneerd” zijn, zodat kleine veranderingen in de input worden vergroot in de output. De onnauwkeurigheden zijn veel minder duidelijk met een grotere nauwkeurigheid van de weergave van de getallen, maar ze zijn nog steeds aanwezig, niettemin. Er zijn ook enkele esoterische beperkingen in het gebruik van getallen die geldig zijn maar niet in drijvende komma kunnen worden weergegeven, zoals tan(π/2), maar deze zullen waarschijnlijk alleen wiskundigen prikkelen.

SQL Server floating point datatypes

de SQL-standaard heeft drie floating point, approximate data types, REALDOUBLEPRECISION en FLOAT(n). SQL Server voldoet hiermee, behalve dat het geen DOUBLEPRECISION datatype heeft, waarbij in plaats daarvan FLOAT(53) wordt gebruikt. De FLOAT(24) en FLOAT(53) datatypes komen overeen met Binary32 (Single) en Binary64 (double) in de IEEE 754 standaard, en worden opgeslagen in 4 en 8 bytes, en 7 en 16 cijfers, dienovereenkomstig. Ze zijn nuttig wanneer het belangrijk is dat berekeningen hetzelfde resultaat opleveren als een toepassing die gebruik maakt van het.NET framework dat ook IEEE 754 gebruikt. Het dubbele precisietype is ook vereist wanneer getallen in hun omvang het maximum overschrijden dat is toegestaan door het DECIMAL datatype (38 cijfers), hoewel met verlies in precisie. Geschatte getallen kunnen natuurlijk niet betrouwbaar worden gebruikt in gelijkheidstoets, zoals eenWHERE clausule.

berekeningen met behulp van het echte datatype (enkele precisie)

Ik zal hetREAL datatype uitproberen. Het FLOAT(24) datatype, of kleiner, reageert op dezelfde manier. Het eerste wat je moet onthouden bij het experimenteren met floating point getallen in SQL Server is dat SSMS een floating point getal weergeeft op een manier die kleine verschillen verhult. Bijvoorbeeld:

1
SELECT Convert(REAL,0.100000001490116119384765625)

…geeft 0.1

om nauwkeuriger te zien welke waarde wordt opgeslagen in een floating point getal, moet je de functie str() gebruiken, waarbij je de precisie specificeert die je eigenlijk wilt.

1
2
3
4
5

/*ik ben een beetje oneerlijk hier omdat het decimale getal 0.1 is niet representatief
in floating-point; de exacte binaire representatie zou een “1100” reeks
voortgezette eindeloos:*/
VERKLAREN @FirstApproximate REAL = 0.1
SELECTEER Str(@FirstApproximate,20,16) –moet 0 zijn.100000001490116119384765625

Dit is al alarmerend. We hebben immers te maken met gegevens met miljoenen rijen, zodat kleine fouten zich opstapelen, tenzij ze, zoals ‘bankiers afronding’, gemiddeld worden. Die fout ligt al dicht bij de’ penny in a million pounds ‘ (1/ 240000000) die ik in de inleiding noemde!

laten we 0.1 vermijden en het neerzetten op een freak of floating point. Hoe zit het met het delen van 1 door 3. Dit kan toch geen probleem zijn?

1
2
3
4
5

met VERMELDING van @uitbetalingen REAL = 1
onder VERMELDING van @deler REAL =3
SELECTEER Str(@uitbetalingen /@deler,20,16) als quotiënt
–produceert 0.3333333432674408
–moet worden 0.3333333333333333

Oeps. Het had het mis. OK, het is een kleine fout maar denk aan mijn verhaal over de bankiers. Een antwoord is goed of fout, er zijn geen grijstinten voor de mannen in grijze pakken. In business school, er is slechts een teek en een kruis. Geen teken dat ‘dichtbij genoeg’ betekent.

een eenvoudige test bestaat uit het delen van één door getallen van één tot twintig. Wat kan er mis gaan?

We kunnen de resultaten van de drijvende komma-en numerieke berekening opslaan, beide geconverteerd naar tekenreeksen en we vergelijken de tekenreeksen (wees gewaarschuwd dat STR() een voorloopruimte kan plaatsen die zorgt voor een complicatie).

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.Quotiëntnumeriek), 16)

Ugh! Alleen waar de deler 1, 2, 4, 8 of 16 was, was er een correct resultaat.

in het geval u hoopt dat de float op de een of andere manier accuraat was, en de numerieke versie niet, hier is het numerieke quotiënt berekend in Excel:

berekeningen met FLOAT(25) or over (double precision)

Als u double precision floating point gebruikt, FLOAT(25) of over, worden alle tests doorstaan, omdat de functie STR() maximaal zestien plaatsen rechts van de decimaal punt. Als er meer dan 16 zijn, wordt het resultaat afgekapt. Het double precision datatype heeft zestien cijfers, terwijl het single precision datatype zeven heeft. U zult ook hebben gezien dat de enkele precisie datatype krijgt de eerste zeven cijfers rechts. Ook de dubbele precisie krijgt de eerste zestien cijfers goed. We kunnen het aantal uitbreiden om de benadering te zien.

1
2

VERKLAREN @FirstApproximate FLOAT(53) = 10000000000000000.1
SELECTEER Str(@FirstApproximate,40,16) ALS BigNumberWithaDecimal

Dat deel is verdwenen, is het niet? Het is waarschijnlijk slechts een klein verschil, maar in sommige berekeningen, kan het problemen veroorzaken.

conclusie

Floating point rekenkunde is snel en zuinig op opslag, maar geeft een benaderend resultaat. Het is geschikt voor goed geconditioneerde wetenschappelijke toepassingen, maar niet voor financiële berekeningen, wat vereist dat een getal ‘goed’ of ‘fout’is. Het heeft ook het extra nadeel in een database omdat je niet betrouwbaar en consistent twee benaderende getallen op gelijkheid kunt testen.

Het is niet juist om te zeggen dat je nooit floating point getallen moet gebruiken in SQL datatypes of in rekenkunde. Benaderende types zijn er in de SQL-standaard voor een doel. Ik zou tegenwoordig altijd vasthouden aan het double precision floating-point datatype in SQL Server, waar een geschikte eis is. Ze zijn geweldig voor doeleinden zoals het modelleren van weersystemen, of het plotten van trajecten, maar niet voor de soorten berekeningen waarvoor de gemiddelde organisatie waarschijnlijk een database zal gebruiken.

Als u foutief gebruik van deze typen waarneemt, moet u in plaats daarvan overschakelen naar een geschikt DECIMALNUMERIC type. Als je weet dat je floating point rekenkunde nodig hebt en kan uitleggen waarom, dan weet je waarschijnlijk genoeg om de valkuilen van floating point te vermijden, zoals die zich voordeed in de beroemde Patriot missile failure die direct leidde tot 28 doden.