Hallo,
Ik was bezig met een script tot ik steeds allerlei rare excepties terug kreeg die mijn script zelf had gegooid, omdat ik wilde voorkomen dat mijn script een getal groter dan 100 doorkreeg (om wat voor reden dan ook, niet interessant).

Het getal dat ik in het script gooide was echter niet groter dan 100, maar exact 100. Het getal is zo berekend:
var = 100 / 9
getal = 0
getal += var
getal += var
getal += var
getal += var
getal += var
getal += var
getal += var
getal += var
getal += var
Oftewel: (100 / 9) * 9. Reken zelf maar uit, dit geeft echt honderd (misschien geeft het 99.9999... op slechte rekenmachines). Ook in php zou deze berekening exact 100 geven, als je het met (100 / 9) * 9 doet. Als je 9 keer plus doet lijkt hij die in eerste instantie ook te geven (als je echo $getal doet komt er 100 op het scherm te staan). Als je nu echter deze vergelijking maakt: getal > 100, krijg je een 1 terug. Zeer tegenstrijdig dus.

Nu wil ik vragen: Zie ik iets over het hoofd, doe ik iets fout? En zo niet, kunnen jullie het probleem ook eens testen op jullie php-bak, om te kijken of het op iedere versie voorkomt? Hier is een snippet om het probleem te testen:
<?php
$number = 9;
echo 'Het getal = ' . $number . '. Het getal 100 wordt nu gedeeld door $number<br />';
$numberDivided = 100 / 9;
echo 'Het getal is gedeeld, en daar is deze uitkomst uitgekomen: ' . $numberDivided . '.<br />';
$numberProductByStar = $numberDivided * 9;
echo 'Het gedeelde getal is nu weer keer $number gedaan d.m.v. de *-operator. Uitkomst: ' . $numberProductByStar . '.<br />';
$numberProductByKruis = 0;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
echo 'Het gedeelde getal is nu weer keer $number gedaan door er 9 keer $numberDivided bij op te tellen. Uitkomst: ' . $numberProductByKruis . '.<br />';
echo 'Kijk of $numberProductByStar groter is dan 100: ';
echo $numberProductByStar > 100 ? 'Ja' : 'Nee';
echo '.<br />';
echo 'Kijk of $numberProductByKruis groter is dan 100: ';
echo $numberProductByKruis > 100 ? 'Ja' : 'Nee';
?>
Hier zou dit uit moeten komen (als de bug er niet is):
Het getal = 9. Het getal 100 wordt nu gedeeld door $number
Het getal is gedeeld, en daar is deze uitkomst uitgekomen: 11.111111111111.
Het gedeelde getal is nu weer keer $number gedaan d.m.v. de *-operator. Uitkomst: 100.
Het gedeelde getal is nu weer keer $number gedaan door er 9 keer $numberDivided bij op te tellen. Uitkomst: 100.
Kijk of $numberProductByStar groter is dan 100: Nee.
Kijk of $numberProductByKruis groter is dan 100: Nee

Als de bug er wel is dan moet de laatste nee Ja worden.

Als dit echt een bug blijkt te zijn zal ik hem submitten op de bug-track van php.net. Bedankt voor jullie hulp alvast.
Robert, php zou dus niet moeten aangeven dat 100 groter is dan 100, maar eerder kleiner dan 100?

Verder klopt de berekening wel, want als je 10 deelt door 5 krijg je 2, als je dat weer keer 5 doet heb je weer 10.
[edit]Jullie waren mij allebei voor.[/edit]

@Karl: Correct:D En bedankt voor je test.
@Robert: Gedeeltelijk mee eens:
Als je uitgaat van een oneindig aantal 1-en zul je ook een oneindig aantal 9ens krijgen, en kijkend naar de bewijstukken van Karl is dat 100. Dat php niet afrond zou je gelijk in kunnen hebben, maar aan de andere kant: Waarom geeft hij dan niet 99.999... met zijn aantal decimalen terug in plaats van 100?

Maar dat maakt alsnog niet uit, want zoals ik al zij: Mijn vergelijking was dat het getal groter was dan 100 en niet kleiner. Het maakt dus niet uit of het 99.9999... is of 100. In dat geval zou ik altijd een boolean 0 terug krijgen.
@Lasse

Het heeft waarschijnlijk wel iets met de decimalen te maken. Als je round gebruikt (maakt niet uit hoeveel decimalen) gaat het wel goed.
@Robert: Klopt ja, ik had het al geprobeerd met de BC Math extensie van PHP, en die doet het wel goed. En round zal neem ik aan ook wel werken. Punt is echter dat het wel een bug is, en dat het beter wel kan worden gerapporteerd, zodat de volgende die zoiets als ik doe probeert niet eerst uren naar het probleem hoeft te zoeken...

[edit]Bedankt voor jullie reacties. Ik zal het morgen rapporteren. Mocht er nog iemand kunnen bewijzen dat het geen bug is, voel je geroepen...[/edit]
Het klopt iig niet. Ik weet niet waar php over struikeld, maar als ik [php]bccomp[/php] probeer met wat voor scale dan ook, ik krijg 0 terug, wat betekend:
Returns 0 if the two operands are equal

:-)
Idd bug dus. Kijk wel effe of d'r al niet iets staat. Graag hoor ik er iig nog meer over :-).

Edit:
Waarschijnlijk toch ergens over een decimaal, want als ik:
<?php
if(((100/9)*9)>100) {
echo "Jah";
} else {
echo "Nee";
}
?>

doe, krijg ik nee...
@Karl: Ja het is heel raar. Ik zal dit topic weer omhoog halen als ik meer weet.
Update: Dit blijkt geen bug te zijn!!
Na wat in de bug database te hebben gezocht, en wat onderzoek te hebben gedaan blijkt dit geen bug te zijn.

Dit staat in de php manual onder Floating point numbers:
Warning Floating point precision It is typical that simple decimal fractions like 0.1 or 0.7 cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9. This is due to the fact that it is impossible to express some fractions in decimal notation with a finite number of digits. For instance, 1/3 in decimal form becomes 0.3. So never trust floating number results to the last digit, and never compare floating point numbers for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.
De reden hiervan is dus dat floats in php (en alle andere scripting/programeertalen) in het binaire [url=http://en.wikipedia.org/wiki/IEEE_754]IEEE 745
worden opgeslagen. Dit wil zeggen dat de afrondingsmanieren anders zijn, en bovendien sommige getallen die decimaal een einde hebben, binair oneindig zijn.
De comments op de php manual link die ik gaf zijn trouwens ook interessant leesvoer.

Bewijs dat dit klopt: Voer dit script uit:
<?php
$number = 9;
echo 'Het getal = ' . $number . '. Het getal 100 wordt nu gedeeld door $number<br />';
$numberDivided = 100 / 9;
echo 'Het getal is gedeeld, en daar is deze uitkomst uitgekomen: ' . $numberDivided . '.<br />';
$numberProductByStar = $numberDivided * 9;
echo 'Het gedeelde getal is nu weer keer $number gedaan d.m.v. de *-operator. Uitkomst: ';
printf("%.40f", $numberProductByStar);
echo '.<br />';
$numberProductByKruis = 0;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
$numberProductByKruis += $numberDivided;
echo 'Het gedeelde getal is nu weer keer $number gedaan door er 9 keer $numberDivided bij op te tellen. Uitkomst: ';
printf("%.40f", $numberProductByKruis);
echo '.<br />';
echo 'Kijk of $numberProductByStar groter is dan 100: ';
echo $numberProductByStar > 100 ? 'Ja' : 'Nee';
echo '.<br />';
echo 'Kijk of $numberProductByKruis groter is dan 100: ';
echo $numberProductByKruis > 100 ? 'Ja' : 'Nee';
?>
De uitkomst zal zijn:
Het getal = 9. Het getal 100 wordt nu gedeeld door $number
Het getal is gedeeld, en daar is deze uitkomst uitgekomen: 11.111111111111.
Het gedeelde getal is nu weer keer $number gedaan d.m.v. de *-operator. Uitkomst: 100.0000000000000000000000000000000000000000.
Het gedeelde getal is nu weer keer $number gedaan door er 9 keer $numberDivided bij op te tellen. Uitkomst: 100.0000000000000142108547152020037174224854.
Kijk of $numberProductByStar groter is dan 100: Nee.
Kijk of $numberProductByKruis groter is dan 100: Ja
Als je dus wilt vergelijken met floats zul je de boel dus moeten afronden op een voor jou interessant aantal decimalen.

[edit]En nog deze Decimaal float naar Binair float omzetter, om het zelf te proberen[/edit]
Niet Bumpen:

Twee of meer keer achter elkaar in een topic posten heet bumpen. Bumpen is pas na 24 uur toegestaan en kan een reden zijn voor de admins en moderators om een topic te sluiten. Gebruik indien nodig de knop om je tekst aan te passen.

SanThe.

Reageren