programmering

Hvorfor datamaskinen din ikke alltid oppgir at 0,1 + 0,2 = 0,3

Her får du en grunnleggende innføring i «mysteriet» som kalles for flyttall.

Det er ikke bare enkelt å forstå hvordan datamaskiner gjør beregninger med desimaltall. Det kan gi overraskende resultater.
Det er ikke bare enkelt å forstå hvordan datamaskiner gjør beregninger med desimaltall. Det kan gi overraskende resultater. Illustrasjonsfoto: Colourbox/1STunningArt

Fordypning

Denne artikkelen skraper bare i overflaten på temaet flyttall, men er kanskje nok til å gi ikke-programmerere og nybegynnere en viss forståelse av hva flyttall handler om.

De som ønsker å fordype seg, anbefales ofte å lese artikkelen What Every Computer Scientist Should Know About Floating-Point Arithmetic fra 1991.

Det er en gjennomgang som er både bredere og dypere enn denne artikkelen, men den stiller også langt større krav til leseren.

En noe mer pedagogisk kilde med omtrent samme navn, finnes her.

Moderne datamaskiner jobber vanligvis binært, det vil si at de er basert på totallsystemet med 2 som grunntall, i stedet for titallsystemet (desimal) med tallet 10 som grunntall, noe de fleste mennesker bruker til daglig.

Det er vanligvis problemfritt å konvertere mellom to- og titallsystemene så lenge det er snakk om heltall. Men straks dataene er desimaltall («kommatall»), kan det oppstå unøyaktigheter som kan føre til at beregningene ikke gir det resultatet som kanskje er ventet.

Brøker og primtall

Årsaken til dette henger sammen med hvilke brøker de to tallsystemene kan gjengi helt nøyaktig.

Med titallsystemet kan brøker som 1/2, 1/5 og 1/8 gjengis helt nøyaktig som desimaltall, nærmere bestemt som 0,5, 0,2 og 0,125. Dette gjelder alle brøker som kan faktoriseres med tallene 2 og 5. Dette skyldes at grunntallet 10 også faktoriseres som primtallene 2 og 5 (2 x 5 = 10).

Brøker som 1/3, 1/6 og 1/7 kan derimot ikke representeres nøyaktig med titallsystemet. I stedet får man et tall med en uendelig rekke med siffer bak kommaet.

Med 2 to grunntall

Det samme gjelder med totallsystemet. Der kan grunntallet 2 kun faktoriseres med primtallet 2. Brøker hvor nevneren kan faktoriseres med 2, slik som 1/2, 1/4 og 1/8, kan gjengis med en endelig mengde siffer bak kommaet.

Brøker som 1/5 og 1/10 og 1/100, som kan representeres rent og pent med titallsystemet, blir derimot et tall med en uendelig rekke med siffer bak kommaet, når de gjengis med totallsystemet.

Når en skal gjøre aritmetiske operasjoner på kommatall i titallsystemet på en datamaskin, må tallene først konverteres til totallsystemet, før operasjonene utføres der, og så konverteres tilbake igjen til titallsystemet.

I teksten nedenfor bruker vi både komma og punktum som desimaltegn, avhengig av om det er tall eller programkode som er gjengitt. I tallene bruker vi punktum som tusenskilletegn.

0,1 + 0,2 = ?

Et regnestykke som 0,1 + 0,2 vil svært ofte føre til et noe annet svar enn 0,3 når en utfører denne beregningen i en kompilator eller et tolkeprogram.

Årsaken er at i totallsystemet er de to tallene gjengitt som henholdsvis:

0,00011001100110011001100 … (tallrekken fortsetter i det uendelige)

0,00110011001100110011001 … (tallrekken fortsetter i det uendelige)

Prøv selv her.

Avrunding og rest

Når disse to tallene legges sammen av datamaskinen, for deretter å bli avrundet og konvertert tilbake til titallsystemet, blir ikke svaret nøyaktig 0,3, men ofte en anelse større – typisk 0,30000000000000004.

Et eksempel på dette er kommandoen console.log(.1 + .2); i JavaScript.

Nettstedet 0.30000000000000004.com har en oversikt hva som blir resultatet av 0,1 + 0,2 i en rekke ulike programmeringsspråk når svaret sendes til «standard output».

Slike feil kan også føre til at sammenligningen av verdier som en skulle tro er like, ikke alltid er det:

float a = 0.15 + 0.15

float b = 0.1 + 0.2

Selv om både a og b burde være 0,3, er det ikke sikkert at sammenligningen a == b er TRUE.

Flytende komma

Alle datamaskiner har begrenset minnekapasitet, men skal likevel kunne lagre både svært store og svært små reelle tall i den samme minneposisjonen. For å gjøre dette mulig, benyttes ofte en representasjon med «flytende» komma, gjerne omtalt som flyttall. I praksis vil ethvert tall være oppgitt med vitenskapelig notasjon, med en mantisse multiplisert med 10 opphøyd i en eksponent.

Lyshastigheten i vakuum er på 299.792.458 meter per sekund. Med vitenskapelig notasjon skrives dette som 2,997.924.58 × 108. Mantissen er her 2,997.924.58 og eksponenten er 8. Eksponenten bestemmer plasseringen av det flytende kommaet relatert til begynnelsen på mantissen.

Presisjonsnivåer

Flyttall har blitt implementert på ulike måter i forskjellige datamaskiner, men det vanligste i dag er å basere flyttallsmaskinvaren på IEEE 754-standarden. Den definerer blant annet flere faste presisjonsnivåer for flyttallene. De mest brukte er enkel og dobbel presisjon.

Enkel presisjon tar i bruk 32 bits i minnet. Av disse brukes 24 bits til mantissen, inkludert fortegnsbit, mens 8 bits brukes til eksponenten, som også kan være positiv eller negativ.

Flyttall med enkel presisjon benytter 32 bit i minnet til datamaskinen. Illustrasjon: digi.no
Flyttall med enkel presisjon benytter 32 bit i minnet til datamaskinen.

En mantisse på 24 bits (inkludert fortegnsbit) kan representere et desimaltall med drøyt 7 sifre. Dette er presisjonen eller nøyaktigheten som tilbys, uansett om tallet veldig stort, veldig lite eller noe midt imellom.

Med 8 bits kan eksponenten være i intervallet -126 til 127. Men dette er eksponenten for 2 som grunntall. Det betyr at tallene som kan lagres som flyttall med enkel presisjon kan ligge i området fra cirka 1,2 x 10-38til cirka 3,4 x 1038.

Tidligere i artikkelen er lyshastigheten gjengitt med 9 sifre. Med en presisjon på sju sifre avrundes dette til 2,997.925 × 108, men dette avhenger av avrundingsmetode.

Presisjon på sju sifre er i veldig mange tilfeller nøyaktig nok, men langt ifra alltid.

Dobbel presisjon

Flyttall med dobbel presisjon bruker 64 bits i minnet til datamaskinen. 53 bits brukes til mantissen, inkludert ett til fortegnet. 11 bits brukes til eksponenten.

Med 53 bits i mantissen får man en nøyaktighet på omtrent 16 desimale siffer. Fordi også eksponentsintervallet er større enn med enkel presisjon (−1022 til 1023), kan det lagres tall i størrelsesområdet mellom omtrent 2,2 x 10-308 og omtrent 1,8 x 10308.

Alternativer

Flyttall er ikke de eneste datatypene som kan brukes til å representere desimaltall. Det finnes mer eksakte typer hvor det enten er et fast antall desimaler etter kommaet, eller hvor presisjonen kun er begrenset av størrelsen på det tilgjengelige minnet i datamaskinen.

I utgangspunktet er det tre faktorer som spiller en rolle i tekniske og vitenskapelige beregninger: presisjon, intervall og fart. Dagens datamaskiner har spesialisert maskinvare som kan utføre flyttallsberegninger veldig raskt. I tillegg kan flyttallsberegninger kan gjøres på både temmelig små og temmelig store verdier. Det er bare presisjonen er særlig begrenset.

Årsaken til at flyttall vanligvis brukes framfor de andre metodene, er at fart og intervall som oftest er viktigere enn at nøyaktighet er så stor at den knapt kan måles. Derfor er mengden av flyttallsoperasjoner som en datamaskin kan utføre per sekund (FLOPS), det viktigste ytelsesmålet når man sammenligner regnekraften til superdatamaskiner.

Powered by Labrador CMS