Avatar billede RasmusTheR Seniormester
19. februar 2017 - 18:25 Der er 11 kommentarer

Fejl når der regnes med random tal

Jeg har lavet el lille script som finder to tilfældige tal og trækker dem fra hinanden, men en sjælden gang i mellem regner den fejl:
fx:    
51,8 - 51,2 = 0,59999999999999
74,7 - 73,8 = 0,90000000000001   

Hvorfor gør den det og hvordan undgår jeg det?

$Tal1= mt_rand(200,800);
$Tal2= mt_rand(100,$Tal1);
$Tal1=$Tal1/10;
$Tal2=$Tal2/10;
$Facit= $Tal1 - $Tal2;
Avatar billede acore Ekspert
19. februar 2017 - 18:37 #1
Tal1 og Tal2 er integers indtil du dividerer med 10. Så bliver de float.  en float værdi har en begrænset præcision. Se http://php.net/manual/en/language.types.float.php og linket til "floating point guide".

I det aktuelle eksempel kan du løse det enten ved at vente med at dividere med 10 til du har trukket tallene fra hinanden, eller ved at udskrive facit med et antal decimaler, der er 1-2 mindre end hvad float repræsenterer.
Avatar billede arne_v Ekspert
20. februar 2017 - 04:01 #2
Som hovedregel boer man faktisk undgaa floating point til alt som ikke er maalt med usikkerhed eller beregnet udfra saadant.
Avatar billede acore Ekspert
20. februar 2017 - 07:56 #3
@arne_v:
Men en integer har jo også en begrænset præcision, så selv om man konverterer udregninger fra float til integer, så ender du samme sted: At der i resultatet er en begrænset præcision bestemt af antal cifre i din repræsentation af float eller integer, og af en hensigtsmæssig fremgangsmåde. Eksempelvis skal man tænke sig godt om før man trækker to næsten lige store tal fra hinanden, og regner videre derfra.
Avatar billede arne_v Ekspert
20. februar 2017 - 21:41 #4
Der er stor forskel paa floating point og integer/fixed point.

Integer kan indeholde alle vaerdier som er heltal mellem min og max.

Regne operationer giver matematisk korrekte resultater medmindre man kommer udenfor min eller max.

Fixed point kan indeholde alle vaerdier som har N decimaler mellem min og max.

Regne operationer giver matematisk korrekte resultater medmindre man kommer udenfor min eller max (med visse regler for naar * og / resulterer i flere decmaler end N).

Float point indeholder et endeligt antal decimal tal med varierende antal decimaler ud af et uendeligt antal reelle tal mellem min og max.

Regne operationer giver ikke altid matematisk korrekte resultater idet resultatet nemt kan vaere et af de reelle tal som ikke kan repraesenteres eksakt.

Men disse problemer kan opstaa selv om man er paent indenfor min max intervallet.
Avatar billede acore Ekspert
20. februar 2017 - 22:01 #5
Jeg forstår godt forskellen, men jeg har svært ved i praksis at følge den logik, at "regneoperationer giver matematisk korrekte resultater" i integer/fixed point.

Så snart du introducerer alt andet end plus og minus gælder det kun sjældent. Eksempelvis vil 1 - 1/3 - 2/3 ikke nødvendigvis give nul i fixed point.

Så min pointe er, at man i praksis skal sørge for, at ens kode tager hensyn til det - uanset om man bruger floating eller fixed point. Det gøres fx ved aldrig at checke om et udregnet tal er 0, men om den absolutte værdi er mindre end EPS (hvor EPS er et lille passende tal).
Avatar billede arne_v Ekspert
21. februar 2017 - 03:28 #6
1 - 1/3 - 2/3 i integer giver 1 - 0 - 0 = 1

1.00 - 1.00/3.00 - 2.00/3.00 i fixed point med 2 decimaler og 4/5 afrunding er 1.00 - 0.33 - 0.67 = 0.00

1.00 - 1.00/3.00 - 2.00/3.00 i fixed point med 2 decimaler og nedrunding er 1.00 - 0.33 - 0.66 = 0.01

Man kan nogenlunde foelge resultatet.

Der burde normalt ikke vaere problemer med at lave et almindeligt == test.

1.0 - 1.0/3 - 2.0/3 i floating point er 1.0 eksakt - 1/3 plus minus en lille smule - 2/3 plus minus en lille smule = 0 plus minus en lille smule

Det er meget svaert at gennemskue hvad den "lille smule" er. Hvis man kender systemets FP format og man er villig til at lave nogle lange udregninger, saa kan det beregnes, men det er tungt.

Det vil ofte vaere problematisk at lave et alimindeligt == test.
Avatar billede acore Ekspert
21. februar 2017 - 10:11 #7
Forstår ikke, at du skriver "Der burde normalt ikke vaere problemer med at lave et almindeligt == test". Uanset om du vælger den ene eller den anden afrunding, kan du let finde eksempler, der ikke giver 0. Fx 1- 1/3 - 1/3 - 1/3 = 1.00 - 0.33 - 0.33 - 0.33 = 0.01.

En almindelig == test er problematisk i både fixed og floating point. Derfor skal man altid benytte abs(x) < eps medmindre der er tale om integers.

Et andet praktisk problem må være, at få (jeg kender ingen) sprog tilbyder en fixed point data type.
Avatar billede arne_v Ekspert
21. februar 2017 - 19:01 #8
Jeg havde også et eksempel som ikke gav 0.

Men det vil sjaeldent vaere grund til at betragte 0.01 som vaerende 0.

Vi har et tilfaelde hvor high level business rules (subtraktioner) og low level business rules (afrunding) giver et resultat som er forskellig fra 0. Men det er noget virkeligt som afspejler business rules. Og det vil sjaeldent give mening at ignorere det ved at betragte det som vaerende 0.

Forestil dig at det er et beloeb paa en krone som skal fordeles ligeligt til 3 personer. De faar hver 33 oerer. Saa er der 1 oere tilbage. Der er brug for en business rule som fortaeller hvordan den oere skal haandteres. Der er ikke bruge for noget software der haevder at der ikke er noget tilbage.
Avatar billede arne_v Ekspert
21. februar 2017 - 19:06 #9
Der er sprog med fixed point data typer.

COBOL packed decimal
Java BigDecimal klasse
.NET (C# og VB.NET) decimal type [den er lidt anderledes men den kan bruges til fixed point beregninger]
Avatar billede acore Ekspert
22. februar 2017 - 08:57 #10
Hvis vi taler kroner, giver det mening. Jeg har ofte set integer brugt (og så regnes der i øre). Det giver samme effekt. Men penge er kun en begrænset del af vores verden.

Du skriver "Der er ikke bruge for noget software der haevder at der ikke er noget tilbage.". Der har du misforstået mig. Med din 1 kr der skal deles mellem 3 vil min foreslåede fremgangsmåde netop vise, at der er 1 øre tilbage, for eps skal være 1e-8 eller deromkring, og derfor er abs(rest) < eps sand.

En sidste kommentar herfra - tror vi har været rundt om emnet: Så snart du introducerer matematiske funktioner eller noget der er rundt ender du alligevel med ikke-absolutte tal. Eksempelvis kan du ikke beregne arealet af en trekant ud fra sidelængderne uden at ende der. Og så er der potentielt en residual og du kan ikke bruge ==
Avatar billede arne_v Ekspert
26. februar 2017 - 18:27 #11
Penge er kun en enkelt ud af et uendeligt antal ting. Men penge er nok 95-99% af fixed point brug.

Man faar ikke fejl ved at bruge abs(x) < eps med fixed point, men det er unoedvendigt. x == 0 virker lige saa godt.

I fixed point med 2 decimaler gaar tallene ... -0.02, -0.01, 0.00, 0.01, 0.02 ....

Med fixed point med 2 decimaler vil x == 0 og abs(x) < eps altid give samme resultat for eps mindre end 0.01. Saa der er ingen grund til ikke bare at bruge ==.

(hvis eps er stoerre end 0.01 saa er fixed point med 2 decimaler naeppe et godt valg af data type)
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

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