Dette indlæg er alene udtryk for skribentens egen holdning.

6,1*7 er ikke 42,7

2. november 2013 kl. 16:2324
Artiklen er ældre end 30 dage

Eller dvs. algebraisk, når vi regner med uendelig stor præcision, er 6,1*7 selvfølgelig lig med 42,7. Men hvis vi f.eks. beder Matlab verificere dette:

Illustration: Privatfoto.

viser det sig, at 42,7-6,1*7 ikke er lig med nul, men derimod ca. 7*10^(-15). Empirisk kan dette forstås ved at spørge Matlab, hvad 42,7 og 6,1*7 er:

og som det ses afviger begge disse tal fra 42,7 på den femtende decimal, hvilket forklarer afvigelsen mellem tallene, ifølge Matlab, fra det første billede.

Artiklen fortsætter efter annoncen

Dette er en af de første ting, man stifter bekendtskab med, når man lærer at lave numeriske beregninger på en computer; computere har kun en endelig præcision og kan derfor kun repræsentere et endeligt antal tal. Det tilfældigt valgte tal 42,7, med nuller på alle følgende decimalpladser, hører i opsætningen i Matlab fra ovenfor ikke til den mængde af tal, som computeren "kender" - hvilket tallene 42,700000000000003 og 42,699999999999996 derimod gør. Og eftersom disse tal for de fleste praktiske formål er lig med 42,7, accepterer vi, at computeren bruger disse og ikke den eksakte værdi 42,7.

Når man laver mange beregninger, kan disse afrundingsfejl - som i eksemplet ovenfor ledte til, at vi accepterede 7*10^(-15) som værende ca. lig nul - akkumulere; ved hver enkelt beregning introduceres potentielt små unøjagtigheder, og hvis disse adderer uheldigt, bliver det sluttelige resultat, som f.eks. kunne forventes at være lig nul, væsentlig større end afrundingsfejlen fra den enkelte beregning (~10^(-15)). Og så kan man komme i tvivl; har afrundingsfejlene akkumuleret, eller er der en fejl et andet sted? Hvor lille skal et tal være for, at det beregningsmæssigt kan siges at være nul? Man er på dette sted nødt til at vælge en tolerance og løbende forholde sig til, om denne tolerance er valgt tilpas, eller om den skal justeres - f.eks. baseret på om der kommer rimelige og sandsynlige resultater ud med dette valg af tolerance.

I mit ph.d. projekt på DTU Fotonik laver jeg computersimuleringer til at analysere lysudbredelse i mikro- og nanostrukturerede materialer - og ovenstående problematik er således vigtig at forholde sig til og undersøge, når computerkoden og -algoritmerne udvikles. Vælges en nultolerance for lavt eller for højt, risikerer jeg, at beregningerne bliver numerisk ustabile, f.eks. pga. eksponentielt voksende tal.

Så på samme måde som at debugging er en vigtig del af processen i programmering og algoritmeudvikling, er det centralt at forstå betydningen af en computers endelige præcision. Eller med andre ord at 6,1*7 ikke altid er lig 42,7.

24 kommentarer.  Hop til debatten
Debatten
Log ind eller opret en bruger for at deltage i debatten.
settingsDebatindstillinger
24
16. november 2013 kl. 02:05

Jeg kender ikke matlab, men ved ethvert værktøj, der arbejder med fysiske størrelser, skal du kunne angive nøjagtigheden af input, således antallet af bits i computeren vælges efter dette, og der skal kunne udregnes nøjagtigheden af output, hvor der også tages hensyn til den mangel på præcision, som skyldes computeren.

Det er intet problem, at anvende algoritmer, så der kan regnes præcist, trods det ikke kan skrives eksakt i totals systemet, så længe at du kan beskrive det som brøker.

Ved ikke, om der findes metoder, der kan håndtere tal som pi og e eksakt. Måske kunne det gøres med BBP notationen: http://en.wikipedia.org/wiki/Bailey%E2%80%93Borwein%E2%80%93Plouffe_formula

23
16. november 2013 kl. 00:14

Der kan være en idé nogle gange at sætte små tal til nul. De såkaldte denormale numre (eller hvad det nu hedder på dansk) har jeg oplevet kan have en betydelig effekt på performance. Det er dem under realmin (2.2251e-308)

NMF-algoritmen kan have problemer (fordi visse parametrene langsomt går mod nul) og et eller andet sted i min matlab-kode tvinger jeg "tilpas" små tal til nul.

Wikipedia har lidt om denormale numre uden at der er konkrete eksempler:

https://en.wikipedia.org/wiki/Denormal_number

22
15. november 2013 kl. 23:51

Det hjælper vel ikke at beregne med nok så mange (endelige) decimaler hvis ikke man ved hvor meget og i hvilken retning fejlene akkumulere sig.

På et eller andet tidspunkt i gamle dage var der vist nogle der her på DTU beskæftigede sig med interval-aritmetik: I 2002 kunne man komme på "Interval Arithmetic Training For Beginners" kursus :-)

Jeg kan se at Python har et modul til interval-aritmetik (pyinterval) baseret på CRlibm. Jeg har ikke erfaring med det, men hvis ellers jeg forstår det korrekt kan man kalde det på denne måde:

interval[427] / 10 - interval(61)/10 * 7 interval([-1.4210854715202004e-14, 1.4210854715202004e-14])

Stefano Taschini viser i et paper en interessant funktion hvor 'Decimal' og 'gmpy' modulerne ikke virker helt godt: http://conference.scipy.org/proceedings/scipy2008/paper_3/full_text.pdf

Jeg ved ikke hvor godt det er i forhold til Matlab og INTLAB.

21
5. november 2013 kl. 09:15

Der gik ikke lang tid før dårlige lommeregnere adskilt fra de gode med denne simple opgave: [latex] 7^2 / 7 [/latex] Gode lommeregnere gav resultatet 7 Dårlige, resultatet 6,9999999

19
4. november 2013 kl. 16:54

Torben, jeg vil nok mene dit eksempel fortsat drejer sig om endelig præcision, eller approksimation. For at være lidt irriterende, så kan grænsen sættes højt men ikke ubegrænset :-) Man kan jo i softwaren naturligvis udvide mængden af disponible tal udover det hardwaren tilbyder så meget man ønsker, for en pris i regnehastighed, men epsilon (omend mindre) kan du jo desværre aldrig komme af med.

18
4. november 2013 kl. 16:34

Har avancerede finansfolk ikke behov for at regne med rødder, sidder jeg og tænker? F.eks at tage kvadratroden af et eller andet. De reelle tal er jo vores egentlige problem. Dem er man nødt til altid at approksimere, da de af natur kun kan repræsenteres som grænseværdien til en uendelig konvergent række. Dermed kommer vi aldrig til at slippe for diciplinen numerisk analyse. Det vil i hvertfald kræve en hardwarearkitektur, der arbejder med symbolsk matematik, som i eksempelvis Maple. Det er nok lidt meget at forlange (lige foreløbigt). ;-)

17
4. november 2013 kl. 14:37

Som Jesper Udby skriver ovenfor, så kan man ikke repræsentere decimal brøker præcist fordi man ikke kan repræsentere en tiendedel præcist med binære brøker (fordi 1/10-del primtalfaktoreres til 1/2 gange 1/5 og 1/5 kan ikke repræsenteres af en sum af 1/2^y brøker).

Hvis man vil have præcision for sine tiendedele (og det vil man gerne i f.eks finansverdenen) bruger man enten et decimalt floating point format, som man finder i f.eks IBM's mainframe hardware), eller fixed precision, hvor man regner i heltal of forskyder decimalpunktet med en given faktor og dermed kvantiserer afrundingsfejlen til et acceptabelt lavt niveau; Typisk regner man i PIP (hundredele øre/cent). Med 64 bit integers kan man således repræsenterer 9,2 x 10¹⁴ af en given møntfod med et PIP's præcision, normalt nok undtagen når man skal regne på USA's gæld i Albanske Leks

16
4. november 2013 kl. 10:34

Der findes biblioteker til at regne med kommatal med ubegrænset præcision. Grundlæggende giver man et epsilon for resultatet, og så beregnes mellemresultater med den nødvendige præcision. Typisk beregnes cifre efter behov, så man beder om flere cifre i mellemresultaterne, hvis man ikke har nok. Jeg har også set metoder baseret på kædebrøker.

Ulempen ved disse metoder er, at det tager ekstremt lang tid at lave beregninger.

15
4. november 2013 kl. 10:27

from decimal import * float(Decimal('42.7')-Decimal('6.1')*7) 0.0

-Eivind

14
4. november 2013 kl. 10:07

1/10 kan ikke repræsenteres eksakt uanset hvor mange bits du bruger.

sagtens - men ikke med alm. floating point ;) 1/7 kan heller ikke skrives eksakt i decimalsystemet på normal vis, men hvis man holder styr på det enten som brøker af heltal eller ved at benytte sig af at mange kommatal fra brøker har en periodicitet, så er det absolut muligt at repræsentere tallet (men ikke nødvendigvis let at regne videre på)....

1/7 = 0,(142857) hvor det i parantesen gentages uendeligt (når man er nået til 7 er der en rest på 1 ligesom efter det første 0)

eller 1/10 binært: 1/1010 = 0,0(0011) - her er en rest på 10 (2) efter det sidste 1

pointen i indlægget er dog yderst relevant, som flere også gør opmærksom på: kend styrkerne og begrænsningerne i dit værktøj, og brug den viden til at løse din opgave.

13
4. november 2013 kl. 02:12

Din opgave, Ole, kan først løses hvis der er en præmis. 1) Er denne en eksakt deling, lige meget til hver, er det 50 ører til hver. 2) Er præmissen en deling af 101 øre med afrunding, er det enten 50/51 eller 51/50.

12
3. november 2013 kl. 22:13

Helt generelt, plejer jeg simpelthen at gange størrelserne op, så man kan nøjes med at arbejde med heltalsaritmetik.

Nu fik du en tommel nedad og der er da også nogle åbenlyse begrænsninger ved metoden, men det er faktisk ikke ualmindeligt at programmer der arbejder med penge regner i ører.

Det er lidt et sjovt problem. Hvis man f.eks. skal dele et beløb i to, hvor meget får hver så? Hvis det er 101 ører, så kunne svaret være 101/2 = 50,5 ører så enten 50 eller 51 ører afhængigt af hvilken vej man afrunder (for man kan som regel ikke få lov til at give nogen en halv øre). Men hvis begge får det samme, passer pengene ikke. Så man er nok nødt til at give den ene 51 og den anden 101 - 51 = 50 ører. Problemet kan f.eks. forekomme hvis man skal beregne moms ud af en vare med en skæv pris.

11
3. november 2013 kl. 12:55

2^53...

10
3. november 2013 kl. 12:49

Ja, det er rigtigt, ved 2^52 (for double) er der ikke længere bits nok, men det er jo en anden problemstilling. 1/10 kan ikke repræsenteres eksakt uanset hvor mange bits du bruger.

9
3. november 2013 kl. 10:28

..begrænsningerne ved sit værktøj (og sine modeller), kan fejle ganske grusomt.

"Numerisk Analyse" og specielt afsnittene om fejlvurdering og fejludbredelse var absolut et af mine bedste fag på Uni. Ikke mindst hjulpet på vej af en meget sympatisk underviser, der desværre døde alt for tidligt.

8
2. november 2013 kl. 21:32

Jeg læste et sted at man havde kørt en klimamodel på forskellige platforme og fået vidt forskellige resultater ud af det. Det minder om Lorentz undersøgelse/opdagelse af kaos. Vi bør åbenbart hele tiden mindes om at komputere har deres eget liv ude på de sidste decimaler, selvom de gør det lynhurtigt.

5
2. november 2013 kl. 18:26

Brug et sprog der understøtter brøker, og repræsentér alle tal som brøker. Problem løst: user=> (* (/ 61 10) 7) 427/10 user=> (float 427/10) 42.7

Nu må jeg straks indrømme at jeg aldrig har arbejdet med matlab, men det skulle undre mig meget hvis den ikke har en funktion til begrænsning af antallet af betydende cifre efter kommaet.

Det kan jo så give problemer i de situationer hvor en beregning ikke giver et pænt resultat.

4
2. november 2013 kl. 18:00

Jesper Udby, det er nu ikke helt rigtigt at det kun gælder decimaltal. Hvis man kommer tilstrækkeligt højt op, cirka 10^16, gælder det også heltal (forudsat at vi stadig taler doubles/floats selvfølgelig)

3
2. november 2013 kl. 17:37

Nu må jeg straks indrømme at jeg aldrig har arbejdet med matlab, men det skulle undre mig meget hvis den ikke har en funktion til begrænsning af antallet af betydende cifre efter kommaet.

Helt generelt, plejer jeg simpelthen at gange størrelserne op, så man kan nøjes med at arbejde med heltalsaritmetik. D.v.s som her f.x.

a=61; b=70; 427 - a*b

Så tror jeg at selv matlab vil finde resultatet 0, som vi selvfølgelig nu skal huske at dividere med 10 :-)

Nu afhænger dette selvfølgelig af hvilke videre kalkulationer man skal lave, men generelt kan man som regel altid bryde et udtryk ned i nogle dele som så hver for sig kan "konverteres" til heltalsaritmetik, hvis resultat kan indgå i de videre beregninger.

2
2. november 2013 kl. 17:29

Det kommer forhåbentligt ikke som en overraskelse at computere – også i dag – regner binært.

Og det er et faktum at visse tal ikke lader sig repræsentere eksakt i totalssystemet (f.eks. 1/10) fuldstændig som visse tal ikke gør det i titalssystemet (f.eks. 1/3). Det gælder nu kun decimaltal, så 1+1 er stadig 2...

Det ændrer tiden ikke noget på – så skal man regne eksakt med decimaltal må man ty til andre metoder. Her er det så en afvejning af performance kontra præcision. Hvis det skal gå stærkt – og det skal det ofte i simuleringer og tilsvarende – må man benytte de ”indbyggede” IEEE typer og acceptere at der kan være fejl på sidste decimal og af og til være lidt kreativ i sin formulering af problemet, så man ikke render ind i problemer der ”bygger sig op”.

En almindelig begynderfejl er at regne økonomi i disse IEEE typer (typisk kaldet ”float” og ”double”). Det går galt med det samme regnskabschefen får regnet efter og konstaterer der mangler en øre, selvom regnestykket er nok så simpelt...

1
2. november 2013 kl. 17:03

... da jeg blev mat/fys student i '70erne måtte vi ikke bruge elektroniske regnemaskiner til eksamen - selv om jeg faktisk havde købt/bygget en der havde alle fire regningsarter. Men vi studerende fik et fornuftigt forhold til 'regnenøjagtighed', når vi skulle bruge regnestok, logaritmetabeller og meget andet til at komme frem til resultatet. Vi fik hurtigt lært at 1+1 ikke nødvendigvis er 2,0000000..., når man bruger en regnemaskine.

Når vi afleverede vores fysikrapporter var det også obligatorisk, at der var et afsnit om usikkerheden på det opnåede resultat - hvor vi både skulle beskrive fysiske og regnemæssige usikkerheder.

Langt senere har jeg arbejdet sammen med post.doc's i fysik - ældre end mig - som ikke kunne forstå dette grundlæggende princip om 'sund fornuft', når man skal vurdere sine resultater af en måling. Det undrede mig dybt, men jeg er jo også bare ingeniør.

Så respekt til dig, Jakob, for at bringe emnet på bane igen. Det er tilsyneladende stadig relevant :-)