Precisie en nauwkeurigheid in berekeningen met drijvende komma
Origineel KB-nummer: 125056
Samenvatting
Er zijn veel situaties waarin precisie, afronding en nauwkeurigheid in berekeningen met drijvende komma kunnen werken om resultaten te genereren die verrassend zijn voor de programmeur. Ze moeten de vier algemene regels volgen:
In een berekening met zowel enkele als dubbele precisie is het resultaat meestal niet nauwkeuriger dan één precisie. Als dubbele precisie vereist is, moet u er zeker van zijn dat alle termen in de berekening, inclusief constanten, met dubbele precisie zijn opgegeven.
Ga er nooit vanuit dat een eenvoudige numerieke waarde nauwkeurig wordt weergegeven in de computer. De meeste drijvende kommawaarden kunnen niet precies worden weergegeven als een eindige binaire waarde. Is
.0001100110011...
bijvoorbeeld.1
in binair (het wordt voor altijd herhaald), zodat het niet met volledige nauwkeurigheid kan worden weergegeven op een computer met behulp van binaire rekenkunde, die alle pc's omvat.Ga er nooit van uit dat het resultaat nauwkeurig is tot het laatste decimaalteken. Er zijn altijd kleine verschillen tussen het 'waar'-antwoord en wat kan worden berekend met de eindige precisie van een drijvende-kommaverwerkingseenheid.
Vergelijk nooit twee drijvende-kommawaarden om te zien of ze gelijk of niet gelijk zijn. Dit is een gevolg van regel 3. Er zijn bijna altijd kleine verschillen tussen getallen die 'gelijk' moeten zijn. Controleer in plaats daarvan altijd of de getallen bijna gelijk zijn. Met andere woorden, controleer of het verschil ertussen klein of onbeduidend is.
Meer informatie
Over het algemeen zijn de hierboven beschreven regels van toepassing op alle talen, inclusief C, C++ en assembler. In de onderstaande voorbeelden ziet u enkele van de regels met behulp van FORTRAN PowerStation. Alle voorbeelden zijn gecompileerd met BEHULP van FORTRAN PowerStation 32 zonder opties, met uitzondering van de laatste, die is geschreven in C.
Voorbeeld 1
In het eerste voorbeeld ziet u twee dingen:
- Dat FORTRAN-constanten standaard één precisie zijn (C-constanten zijn standaard dubbele precisie).
- Berekeningen die enkele precisietermen bevatten, zijn niet veel nauwkeuriger dan berekeningen waarin alle termen één precisie hebben.
Nadat y is geïnitialiseerd met 1,1 (één precisieconstante), is y net zo onnauwkeurig als één precisievariabele.
x = 1.100000000000000 y = 1.100000023841858
Het resultaat van het vermenigvuldigen van één precisiewaarde met een nauwkeurige dubbele precisiewaarde is bijna net zo slecht als het vermenigvuldigen van twee enkele precisiewaarden. Beide berekeningen hebben duizenden keren zoveel fouten als het vermenigvuldigen van twee dubbele precisiewaarden.
true = 1.320000000000000 (multiplying 2 double precision values)
y = 1.320000052452087 (multiplying a double and a single)
z = 1.320000081062318 (multiplying 2 single precision values)
Voorbeeldcode
C Compile options: none
real*8 x,y,z
x = 1.1D0
y = 1.1
print *, 'x =',x, 'y =', y
y = 1.2 * x
z = 1.2 * 1.1
print *, x, y, z
end
Voorbeeld 2
In voorbeeld 2 wordt de kwadratische vergelijking gebruikt. Het toont aan dat zelfs berekeningen met dubbele precisie niet perfect zijn en dat het resultaat van een berekening moet worden getest voordat deze afhankelijk is van of kleine fouten drastische resultaten kunnen hebben. De invoer voor de vierkantswortelfunctie in voorbeeld 2 is slechts licht negatief, maar is nog steeds ongeldig. Als de berekeningen met dubbele precisie geen kleine fouten bevatten, is het resultaat:
Root = -1.1500000000
In plaats daarvan wordt de volgende fout gegenereerd:
runtime-fout M6201: MATH
- sqrt: DOMEINfout
Voorbeeldcode
C Compile options: none
real*8 a,b,c,x,y
a=1.0D0
b=2.3D0
c=1.322D0
x = b**2
y = 4*a*c
print *,x,y,x-y
print "(' Root =',F16.10)",(-b+dsqrt(x-y))/(2*a)
end
Voorbeeld 3
Voorbeeld 3 laat zien dat vanwege optimalisaties die optreden, zelfs als optimalisatie niet is ingeschakeld, waarden tijdelijk een hogere precisie kunnen behouden dan verwacht en dat het niet verstandig is om twee waarden met drijvende komma op gelijkheid te testen.
In dit voorbeeld zijn twee waarden gelijk en niet gelijk. Bij de eerste IF bevindt de waarde van Z zich nog steeds op de stack van de coprocessor en heeft deze dezelfde precisie als Y. Daarom is X niet gelijk aan Y en wordt het eerste bericht afgedrukt. Op het moment van de tweede IF moest Z uit het geheugen worden geladen en had daarom dezelfde precisie en waarde als X, en het tweede bericht wordt ook afgedrukt.
Voorbeeldcode
C Compile options: none
real*8 y
y=27.1024D0
x=27.1024
z=y
if (x.ne.z) then
print *,'X does not equal Z'
end if
if (x.eq.z) then
print *,'X equals Z'
end if
end
Voorbeeld 4
Het eerste deel van voorbeeldcode 4 berekent het kleinst mogelijke verschil tussen twee getallen dicht bij 1,0. Dit doet u door één bit toe te voegen aan de binaire weergave van 1.0.
x = 1.00000000000000000 (one bit more than 1.0)
y = 1.00000000000000000 (exactly 1.0)
x-y = .00000000000000022 (smallest possible difference)
Sommige versies van FORTRAN ronden de getallen af wanneer ze worden weergegeven, zodat de inherente numerieke onnauwkeurigheid niet zo duidelijk is. Daarom zien x en y er hetzelfde uit wanneer ze worden weergegeven.
Het tweede deel van voorbeeldcode 4 berekent het kleinst mogelijke verschil tussen twee getallen dicht bij 10,0. Dit doet u opnieuw door één bit toe te voegen aan de binaire weergave van 10.0. U ziet dat het verschil tussen getallen bij 10 groter is dan het verschil bij 1. Dit toont het algemene principe aan dat hoe groter de absolute waarde van een getal, hoe minder nauwkeurig het kan worden opgeslagen in een bepaald aantal bits.
x = 10.00000000000000000 (one bit more than 10.0)
y = 10.00000000000000000 (exactly 10.0)
x-y = .00000000000000178
De binaire weergave van deze getallen wordt ook weergegeven om aan te geven dat ze slechts 1 bit verschillen.
x = 4024000000000001 Hex
y = 4024000000000000 Hex
Het laatste deel van voorbeeldcode 4 laat zien dat eenvoudige niet-herhalende decimale waarden vaak alleen in binair kunnen worden weergegeven door een herhalende breuk. In dit geval x=1,05, waarvoor een herhalende factor CCCCCCCC vereist is.... (Hex) in de mantissa. In FORTRAN wordt het laatste cijfer 'C' naar boven afgerond op 'D' om de hoogst mogelijke nauwkeurigheid te behouden:
x = 3FF0CCCCCCCCCCCD (Hex representation of 1.05D0)
Zelfs na afronding is het resultaat niet perfect nauwkeurig. Er is een fout opgetreden na het minst significante cijfer, dat we kunnen zien door het eerste cijfer te verwijderen.
x-1 = .05000000000000004
Voorbeeldcode
C Compile options: none
IMPLICIT real*8 (A-Z)
integer*4 i(2)
real*8 x,y
equivalence (i(1),x)
x=1.
y=x
i(1)=i(1)+1
print "(1x,'x =',F20.17,' y=',f20.17)", x,y
print "(1x,'x-y=',F20.17)", x-y
print *
x=10.
y=x
i(1)=i(1)+1
print "(1x,'x =',F20.17,' y=',f20.17)", x,y
print "(1x,'x-y=',F20.17)", x-y
print *
print "(1x,'x =',Z16,' Hex y=',Z16,' Hex')", x,y
print *
x=1.05D0
print "(1x,'x =',F20.17)", x
print "(1x,'x =',Z16,' Hex')", x
x=x-1
print "(1x,'x-1=',F20.17)", x
print *
end
Voorbeeld 5
In C zijn zwevende constanten standaard dubbels. Gebruik een 'f' om een floatwaarde aan te geven, zoals in '89,95f'.
/* Compile options needed: none
*/
#include <stdio.h>
void main()
{
float floatvar;
double doublevar;
/* Print double constant. */
printf("89.95 = %f\n", 89.95); // 89.95 = 89.950000
/* Printf float constant */
printf("89.95 = %f\n", 89.95F); // 89.95 = 89.949997
/*** Use double constant. ***/
floatvar = 89.95;
doublevar = 89.95;
printf("89.95 = %f\n", floatvar); // 89.95 = 89.949997
printf("89.95 = %lf\n", doublevar); // 89.95 = 89.950000
/*** Use float constant. ***/
floatvar = 89.95f;
doublevar = 89.95f;
printf("89.95 = %f\n", floatvar); // 89.95 = 89.949997
printf("89.95 = %lf\n", doublevar); // 89.95 = 89.949997
}
Feedback
https://aka.ms/ContentUserFeedback.
Binnenkort beschikbaar: In de loop van 2024 zullen we GitHub Issues geleidelijk uitfaseren als het feedbackmechanisme voor inhoud. Het wordt vervangen door een nieuw feedbacksysteem. Zie voor meer informatie:Feedback verzenden en weergeven voor