Articles

farerne ved at bruge Float eller Real datatyper

Der er en gammel vittighed om flydende aritmetik:

“hvis jeg skærer en kage i tre, er hvert stykke 0,33 af kagen. Hvis jeg holder alle tre stykker sammen igen, giver det mig 0,99 af kagen. Hvor er resten af kagen blevet af?”
– ” enkel. Det er den lille smule fast på kniven ”

flydende aritmetik handler om at tolerere og styre tilnærmelse for at undgå overløbsfejl i beregningerne. I den virkelige verden bekymrer vi os normalt om præcision i antal og vil i stedet ofre plads og ressourcer for at undgå overløb.

mens videnskaben fungerer lykkeligt inden for en fejlmargin, betyder præcision i forretningsregnskab. Da jeg var cub-programmør, skrev jeg engang, hvad jeg troede var en perfekt passende måde at beregne overskuddet på børsmægleraftaler. I en million pund, det var højst en krone eller to ud. Jeg var godt tilfreds. Det brugte beregningerne i PL / 1-kompilatoren, som vi brugte på det tidspunkt til at udvikle finansielle pakker. Jeg viste dem den fint udformede applikation, og de blev forfærdet. En øre ud i en million pund syntes de hårdkogte byhandlere at være hensynsløse. De ville ikke have det. Jeg blev tvunget til at skrive en binær-kodet-decimal (BCD) pakke i assembler-kode, der var nøjagtigt nøjagtig.en kode analyse regel (BP023), der vil advare dig om brugen af FLOAT eller REAL datatyper, på grund af de betydelige unøjagtigheder, de kan introducere til den slags beregninger, som mange organisationer rutinemæssigt vil udføre på deres HP Server data.

omtrentlige antal datatyper

flydende aritmetik blev udtænkt på et tidspunkt, hvor det var en prioritet at gemme hukommelse, samtidig med at det gav en alsidig måde at udføre beregninger på, der involverede store tal. Selvom det stadig er nyttigt til mange typer videnskabelige beregninger, især dem, der er i overensstemmelse med dobbeltpræcisions IEEE 754-standarden for flydende aritmetik, er det nødvendigvis et kompromis. Ledetråden er i navnet på denne type data og aritmetik: ‘omtrentlig’. Flydende punktnumre kan ikke nøjagtigt repræsentere alle reelle tal: derudover kan flydende punktoperationer ikke nøjagtigt repræsentere alle aritmetiske operationer. Størrelsen af det tal, de kan holde, er imidlertid langt større, end det er muligt i andre numeriske typer, selvom det ikke altid holdes nøjagtigt.

de problemer, der opstår ved brug af flydende punktberegninger, skyldes afrunding under komplekse beregninger og ses oftest, hvis dataene er ‘dårligt konditioneret’, så små ændringer i input forstørres i output. Unøjagtighederne er langt mindre tydelige med øget præcision af repræsentationen af tallene, men de er stadig til stede, ikke desto mindre. Der er også nogle esoteriske begrænsninger i brugen af tal, der er gyldige, men som ikke kan repræsenteres i floating point, såsom tan(Kurt/2), men disse vil sandsynligvis kun begejstre matematikere.

SL Server floating point datatyper

SL-standarden har tre floating point, omtrentlige datatyper,REALDOUBLEPRECISION ogFLOAT(n). Den har ingen DOUBLEPRECISION datatype ved hjælp af FLOAT(53) i stedet. DatatyperneFLOAT(24) ogFLOAT(53) svarer til Binary32 (Single) og Binary64 (double) i IEEE 754-standarden og gemmes i 4 og 8 byte, og 7 og 16 cifre holdes i overensstemmelse hermed. De er nyttige, når det er vigtigt, at beregninger giver det samme resultat som et program ved hjælp af.Net-rammen, der også bruger IEEE 754. Den dobbelte præcisionstype er også påkrævet, når tal overstiger i deres størrelse det maksimale tilladte af DECIMAL datatype (38 cifre) dog med tab i præcision. Omtrentlige tal kan naturligvis ikke bruges pålideligt i nogen test af lighed, såsom en WHERE klausul.

beregninger ved hjælp af den rigtige datatype (enkelt præcision)

Jeg vil prøveREAL datatype. FLOAT(24) datatype, eller mindre, reagerer på samme måde. Den første ting, man skal huske, når man eksperimenterer med flydende punktnumre i SSMS-serveren, er, at SSMS gengiver et flydende punktnummer på en måde, der skjuler små forskelle. For eksempel:

1
vælg Konverter(ægte,0.100000001490116119384765625)

…giver 0,1

for at se mere præcist, hvilken værdi der er gemt i et flydende punktnummer, skal du bruge funktionen STR() og specificere den præcision, du faktisk ønsker.

1
2
3
5

/*jeg er en lille smule uretfærdigt her, fordi decimaltallet 0.1 ikke kan repræsenteres
i flydende punkt; den nøjagtige binære repræsentation ville have en “1100” sekvens
fortsætter uendeligt:*/
erklære @firstapprocent real = 0.1
vælg str(@firstapprocent,20,16)-skal være 0.100000001490116119384765625

allerede, dette er alarmerende. Vi er, trods alt, beskæftiger sig med data med millioner af rækker, så små fejl vil stable op, medmindre, ligesom ‘bankfolk afrunding’, De gennemsnit ud. Denne fejl er allerede tæt på ‘ penny in a million pounds ‘(1/ 240000000), som jeg nævnte i indledningen!

lad os undgå 0,1 og sætte det ned til en freak af flydende punkt. Hvad med at dividere 1 med 3. Dette kunne bestemt ikke være et problem?

1
2
3
5

angivelse @payoffs REAL = 1
angivelse @divisor Real =3
vælg str(@payoffs /@divisor,20,16) som kvotient
–producerer 0.33333333432674408
–bør være 0.3333333333333333

ups. Det fik det forkert. OK, det er en lille fejl, men husk min historie om bankfolk. Et svar er enten rigtigt, eller det er forkert, der er ingen gråtoner til mændene i grå dragter. På Handelshøjskolen er der kun et kryds og et kryds. Intet tegn, der betyder ‘nær nok’.

en simpel test er at opdele en efter tal fra en til tyve. Hvad kunne gå galt?

Vi kan gemme resultaterne af det flydende punkt og den numeriske beregning, begge konverteret til strenge, og vi sammenligner derefter strengene (advares om, at STR() kan sætte et førende rum, der giver en komplikation).

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.Kvotientnumerisk), 16)

Ugh! Kun hvor divisoren var 1, 2, 4, 8 eller 16 var der et Korrekt resultat.

Hvis du håber, at flyderen på en eller anden måde var nøjagtig, og den numeriske version ikke var, her er den numeriske kvotient beregnet i:

beregninger ved hjælp af FLOAT(25) eller over (double precision)

Hvis du bruger double precision floating point, FLOAT(25) eller over, er testene alle bestået på grund af STR() funktionen tillader maksimalt seksten steder til højre for decimaltegnet. Hvis der er mere end 16, afkortes resultatet. Datatypen med dobbelt præcision har seksten cifre, mens datatypen med enkelt præcision har syv. Du har også set, at den enkelte præcisionsdatatype får de første syv cifre rigtigt. Ligeledes får dobbeltpræcisionen de første seksten cifre rigtigt. Vi kan bare udvide antallet for at se tilnærmelsen.

1
2
erklære @Firsttilnærm FLOAT(53) = 100000000000000000.1
vælg str(@firstomtrent,40,16) som bignumbermedadecimal

den brøkdel er forsvundet, har den ikke? Det er nok kun en lille forskel, men i nogle beregninger kan det forårsage problemer.

konklusion

flydende aritmetik er hurtig og økonomisk ved opbevaring, men giver et omtrentligt resultat. Det er velegnet til velkonditionerede videnskabelige anvendelser, men ikke til økonomiske beregninger, der kræver, at et tal enten er ‘rigtigt’ eller ‘forkert’. Det har også den ekstra ulempe i en database, fordi du ikke pålideligt og konsekvent kan teste to omtrentlige tal for lighed.

det er ikke korrekt at sige, at du aldrig skal bruge flydende punktnumre i datatyper eller i aritmetik. Omtrentlige typer er der i KVL-standarden til et formål. Jeg vil i dag altid holde fast i den dobbelte præcisions flydende datatype i en server, hvor der er et passende krav. De er gode til sådanne formål som modellering af vejrsystemer eller planlægning af baner, men ikke for de typer beregninger, som den gennemsnitlige organisation sandsynligvis vil bruge en database til.

Hvis du ser fejlagtig brug af disse typer, skal du skifte til en passendeDECIMALNUMERIC type i stedet. Hvis du ved, at du har brug for flydende aritmetik og kan forklare hvorfor, ved du sandsynligvis nok til at undgå faldgruberne ved flydende punkt, såsom den, der opstod i den berømte Patriot-missilfejl, der førte direkte til 28 dødsfald.