Avatar billede dottie Nybegynder
19. november 2003 - 23:27 Der er 28 kommentarer og
2 løsninger

Mystisk fejl i lommeregner

Jeg er ved at forsøge at lære Visual Basic og har som mange andre startet med at lave en lommeregner.

Nu har jeg opdaget en mystisk regne-fejl.....

Regne-resultater:
1,55 - 1,5 = 0,05
MEN !!!!!!
2,55 - 2,5 = 04.99999999999998E-02

Jeg har prøvet at downloade andres lommeregnere på nettet, for at se om jeg selv kunne finde fejlen.
Men en masse andre har nøjagtig samme regnefejl.

Kan nogle fortælle mig hvad det er der går galt?

Med venlig hilsen
Dottie
Avatar billede arne_v Ekspert
19. november 2003 - 23:43 #1
Tja.

Der er en vis regne unøjagtighed på floating point tal.

Sådan fungerer en computer.
Avatar billede arne_v Ekspert
19. november 2003 - 23:44 #2
2 strategier til at undgå de "dumme" resultater:

1)  afrund resulatet til et bestemt antal decimaler, så bør du undgå det.

2)  hvis det er beløb så regn i ører og divider først med 100 når det
    skal displayes
Avatar billede dottie Nybegynder
19. november 2003 - 23:55 #3
Det var ikke meningen at resultatet skulle afrundes.
Den skulle jo gerne fungere ligesom enhver anden lommeregner.

Windows egen lommeregner kan sagtens finde ud af at 2,55 - 2,5 = 0,05
Men hvis jeg udregner et længere decimal tal, så skulle den jo gerne vise det med så mange decimaler som muligt.

Jeg har forsøgt at afrunde resultatet til 20 decimaler uden resultatet blev anderledes.
Avatar billede arne_v Ekspert
20. november 2003 - 00:00 #4
20 decimaler er for mange.

Afhængig af hvilken FP der bruges så skal det være enten 6 eller 14.
Avatar billede arne_v Ekspert
20. november 2003 - 00:01 #5
Korrektion: det inkluderer også cifre foran punktum d.v.s. det er cifre ialt.

Det er formentligt de 14 der gælder.
Avatar billede dottie Nybegynder
20. november 2003 - 00:04 #6
Der må da kunne opsættes en eller anden ting der kan få en lommeregner til at regne rigtigt?

Det kan da ikke passe at microsoft mener man IKKE bør regne med mere end 14 decimaler???

Er der nogle som kender en måde at klare dette problem, uden at afrunde?
Avatar billede arne_v Ekspert
20. november 2003 - 00:09 #7
Sådan fungerer floating point på alle computere.

Det er iøvrigt snarere Intel end Microsoft du skal give skylden.
Avatar billede dottie Nybegynder
20. november 2003 - 05:31 #8
vil det sige, at en KORREKT fungerende lommeregner, ikke kan lade sig gøre at lave?

For afrunding til 14 decimaler giver samme problem, hvis regnestykket er
300,55 - 300,5 = 0.05000000000001
Avatar billede arne_v Ekspert
20. november 2003 - 06:41 #9
Ja - fordi nu er der 3 cifre foran decimal punktum.
Avatar billede jpvj Nybegynder
20. november 2003 - 06:45 #10
Naturligvis kan det lade sig gøre at lave en regne maskine, der regner "korrekt". Du skal så ikke benytte dig af "two complements" notationen, men i stedet benytte "noget andet". Jeg vil ikke komme med forslag, da jeg ikke har et kvalificeret bud.
Avatar billede martin_moth Mester
20. november 2003 - 07:30 #11
dottie: Prøv lige at vise lidt af din kode. De værdier du trækker fra hinanden, hvilke variabeltyper gemmer du dem i?
Avatar billede martin_moth Mester
20. november 2003 - 07:36 #12
Jeg må give dig ret:

Dim Tal1 As Double
Dim Tal2 As Double
Dim Resultat As Double
Tal1 = 2.55
Tal2 = 2.5
Resultat = Tal1 - Tal2
MsgBox Resultat

Giver IKKE 0.5, men 04.99999999999998E-02

Du kan naturligvis altid afrunde, men da resultatet er EKSAKT 0.05, regner VB forkert. Hvordan du løser det ved jeg ikke, men det MÅ kunne løses!!!

Dit eksempek svarer til, at 2+2 giver 3.9999999999999999999999999999999999 - og det er jo forkert. Nogle kloge hoveder må tage over :o)
Avatar billede dottie Nybegynder
20. november 2003 - 07:40 #13
martin moth : så prøv lige at skifte tallene ud med 1.55 og 1.5
Så kommer det endnu "sjovere", så kan den nemlig godt regne rigtigt.

Håber stadig på at der er nogle kloge hoveder,
som kan løse dette problem. *SS*
Avatar billede slo Nybegynder
20. november 2003 - 08:39 #14
Først det korte svar: Skift MsgBox Resultat ud med:
MsgBox Round(Resultat, 14)
Avatar billede dottie Nybegynder
20. november 2003 - 08:51 #15
slo : Da udregningen skal bruges til en lommeregner/regnemaskine
så dur det ikke med afrunding.

Prøv dit eget forslag med

Tal1 = 300.55
Tal2 = 300.5


Det giver ikke præcis 0.05 som er det rigtige resultat
Avatar billede slo Nybegynder
20. november 2003 - 08:52 #16
(Og afrunding er allerede foreslået - ovenstående giver dig det rigtige resultat 0.05 uden en masse nuller bagved.)
Og så det lidt længere svar: Som det allerede er blevet pointeret så opfatter VB Tal1, dvs. 2.55, som et floatingpoint number med double precision (der findes også en datatype Single). Dvs. VB opfatter Tal1 som et reelt tal med "dobbel" præcision, og IKKE som et rationelt tal (et tal der kan skrives som en brøk).
Det er ikke muligt at angive sin Datatype som et rationelt tal i de fleste almindelige computersprog - så skal man ud i at regne symbolsk og have fat i et avanceret matematik program som f.eks. Maple. (Det er rent faktisk ikke VB der ikke regner rigtigt - det er nærmere din processor!)
Hvorfor sker "fejlen" så? Den sker fordi VB har lagret tallene som Double's og dermed med en præcision på 14 (tror jeg nok) cifre så 2.55 bliver lagret som 2.55000000000000 og tilsvarende for 2.5. I beregningsrutinerne sker der afrundingsfejl - eller såkaldt maskinpræcision - og det kan man IKKE undgå! Fejlen er ude på allersidste decimal, dvs. den absolutte fejl er lig 0.0000000000001, eller 0.00000000001 procent. Når man tænker over det så er det faktisk meget godt klaret!
Avatar billede slo Nybegynder
20. november 2003 - 08:54 #17
dottie -> Prøv med round(resultat, 12) i stedet - du må selv lige lege med hvad der er et acceptabelt niveau. Husk du har nu tilføjet nogle cifre FORAN decimaltegnet - VB er "ligeglad" med hvor decimaltegnet står når den lagrer tallet. Nu har du tre cifre foran - dermed kan du ikke brug round(resultat, 14) men må gøre afrunden lidt mindre.
Avatar billede dottie Nybegynder
20. november 2003 - 09:03 #18
slo : det er jo en lommeregner.
Der kunne jo også være tastet tallet 3333333333333.55
så skulle jeg jo afrunde med kun 1 decimal.
Det dur da ikke i en lommeregner

Men hvis det virkelig er rigtigt, at dette problem ikke kan løses
uden at bruge Round, så er det faktisk Arne v, der skal have pointene.
Men jeg har jo ikke fået svar på problemet, så hvis Arne v lægger et svar,
så vil jeg dele pointene med ham. Da jeg jo selv havde fundet ud af, at det
var en regnefejl og ikke en programmeringsfejl.
Avatar billede slo Nybegynder
20. november 2003 - 09:10 #19
dottie -> Prøv lige at sætte
tal1 = 3333333333333.55
tal2 = 3333333333333.5
og se hvad der så sker når du trækker de to tal fra hinanden :)
Det er ikke en simpel rutine du efterspø'r - der må lidt benarbejde til hvis du vil have det til at virke "i alle tilfælde"
Avatar billede dottie Nybegynder
20. november 2003 - 09:21 #20
3333333333333.55 - 3333333333333.5
Round(resultat,4) = 0.0498
Round(resultat,3) = 0.05

Så er jeg helt nede på 3 decimaler..... Meget dårlig lommeregner!
Avatar billede slo Nybegynder
20. november 2003 - 09:31 #21
Prøv i øvrigt at taste ovenstående ind på din almindelige lommeregner (hvis du kan!) - jeg har gjort det på en HP regner og den giver resultatet 0.
Hvad jeg mener med benarbejde er at du bliver nødt til at se på resultatet hver gang og derefter finde ud af hvor meget der skal afrundes med.
I øvrigt skrev du at du kun skulle bruge lommeregnereren til at lære lidt VB programmering med - er det ikke at skyde over målet at kræve at den regner (næsten 100 pct) rigtigt i alle tilfælde?
Avatar billede dottie Nybegynder
20. november 2003 - 09:41 #22
Tja, at skyde over målet er måske nok meget muligt i denne programmering.

Men når jeg finder en begrænsning i programmeringen, så er det jo rart at
vide til en anden gang.

Hvis jeg en gang skulle lave et eller andet tosset med at udregne længden til en eller anden planet eller andet hvor man kommer op i meget store tal med mange decimaler, så er det jo godt at vide, at til den slags dur VB ikke.

P.S. Windows egen indbyggede lommeregner :
33333333333333333333.55 - 33333333333333333333.5 = 0.05

3,5555555555555555555555555 - 3,555555555555555555555555 = 0.0000000000000000000000005
Avatar billede martin_moth Mester
20. november 2003 - 10:28 #23
dottie: Sikke en start på VB :o(

Du kan lave en funktion, der afrunder resultat til det antal decimaler input-tallene har. Hvis det er ingeniørberegninger, så kan du argumenterer for, at du ikke kan udtale dig om resultatets præsicion i højere grad end du kender den fra inputtet - så lav en "ingeniør-lommeregner" ;o)

Hvis du ser bort fra denne skæve start, så er VB6 altså et lækkert program. Mist ikke modet.

Og husk nu på at 0,05 er lige så fint et resultat som 0,050000000000000 !
Avatar billede martin_moth Mester
20. november 2003 - 10:29 #24
P.S. Windows egen indbyggede lommeregner giver i følge dig:

33333333333333333333.55 - 33333333333333333333.5 = 0.05
3,5555555555555555555555555 - 3,555555555555555555555555 = 0.0000000000000000000000005

Giver VB egentligt ikke præcis det samme?
Avatar billede slo Nybegynder
20. november 2003 - 10:32 #25
Hvis du i øvrigt laver en funktion der afrunder resultatet til antal decimaler som input-tallene har så kan den jo nok ikke bruges når du skal dividere, fx. 1 divideret med 3. (Skulle jo nødig få nul som resultat!)
Avatar billede arne_v Ekspert
20. november 2003 - 10:34 #26
Floating point tal har en masse gode egenskaber men også nogle lit uheldige.

Det må man bare vende sig til.

Eller lave noget snedigt kode der afrunder til et antal decimaler
der afhænger af hvor mange tal der er foran deciaml punktum.

Det kan godt gøres, men det er ikke trivielt.
Avatar billede martin_moth Mester
20. november 2003 - 10:53 #27
Du kan også putte denne msgbox ind i dit program, så den optræder når brugeren trykker på enten kommategnet eller på divider:

Msgbox "Giv agt. Programmet regner ikke med decimaltegn!!! " & chr(13) & _
      "Der er risiko for at decimaltal kan optræde ved division" & chr(13) & _
      "Derfor er decimaltegn og division udelukket" & chr(13) & chr(13) & _
      "Brug din mobiltelefon eller en lommeregner i stedet for", vbCritical, "Fejl"
Avatar billede dottie Nybegynder
20. november 2003 - 13:58 #28
martin moth : *GGGGG* Jeg kunne jo selvfølgelig også lave en Msgbox
med beskeden :
"Hvorfor tror du der er indbygget en lommeregner i windows? Brug den!" *GGGG*

Ang dette regnestykke:
3,5555555555555555555555555 - 3,555555555555555555555555 =
WB giver resultatet 0 og ikke 0.0000000000000000000000005

Arne v : Du var først med at sige, at det var "regne unøjagtighed på floating point tal." Så læg et svar, så får du halvdelen af pointene.

Så lukker jeg spørgsmålet og accepterer at det ikke "bare" lader sig gøre.
Avatar billede arne_v Ekspert
20. november 2003 - 14:10 #29
svar
Avatar billede martin_moth Mester
20. november 2003 - 14:19 #30
Lav en lille teksteditor i stedet for. Eller noget andet. Så bliver du glad igen, for der laver VB ikke fejl :o)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester