Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[PHP] 1 delen door 2, het resultaat vervolgens ook

Pagina: 1
Acties:

  • Hurm
  • Registratie: Maart 2010
  • Laatst online: 21-12-2014
Ik ben bezig met enkele complexe berekeningen in PHP, waardoor ik denk dat ik tegen de maximum precieze aanloop van PHP in berekeningen. In een simpel voorbeeld kijk ik hoe vaak ik 1 door 2 kan delen, vervolgens het resultaat door 2 delen en dat resultaat weer door 2. Theoretisch gezien gaat dit door tot in het oneindige, maar PHP denkt daar anders over:


PHP:
1
2
3
4
5
6
7
$i = 1;
$counter = 0;

while($i != 0){
    $i = $i/2;
    $counter++;}
echo $counter;



Na 1075 iteraties zal PHP een 0 geven als resultaat. Mijn vraag is nu, waardoor komt dit? Als ik de precision aanpas in de php.ini file, van bijvoorbeeld 14 naar 28, is het resultaat hetzelfde.

Wie kan mij uitleggen waarom ik tegen dit limiet aan loop, waar het door komt en wat ik er eventueel aan/tegen kan doen?

[ Voor 0% gewijzigd door BtM909 op 29-10-2013 14:34 . Reden: je kan [php]-tags gebruiken voor code ;-) ]


  • HenkEisDS
  • Registratie: Maart 2004
  • Laatst online: 15:23
Warning
Floating point precision

Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error propagation must be considered when several operations are compounded.

Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they 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.9999999999999991118....

So never trust floating number results to the last digit, and do not compare floating point numbers directly for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.

For a "simple" explanation, see the » floating point guide that's also titled "Why don’t my numbers add up?"
http://php.net/manual/en/language.types.float.php

  • kaesve
  • Registratie: Maart 2009
  • Laatst online: 16-05 03:04
toevallig zag ik laatst een goede presentatie over hoe er intern met nummers word gewerkt in javascript:

YouTube: Bartek Szopka: Everything you never wanted to know about JavaScript numbers -- JSConf EU 2013

Javascript gebruikt hiervoor een standaard die in meer programmeertalen gebruikt wordt (in ieder geval javascript). Ik weet niet of php exact hetzelfde gebruikt, maar het zal in ieder geval vergelijkbaar zijn.

  • Hurm
  • Registratie: Maart 2010
  • Laatst online: 21-12-2014
Ik snap dat PHP gebruik maakt van floating points. Wat ik echter niet snap is waarom ik tegen die grens aan loop, zelfs als ik de precision verhoog.

  • Hurm
  • Registratie: Maart 2010
  • Laatst online: 21-12-2014
kaesve schreef op dinsdag 29 oktober 2013 @ 12:22:
toevallig zag ik laatst een goede presentatie over hoe er intern met nummers word gewerkt in javascript:

YouTube: Bartek Szopka: Everything you never wanted to know about JavaScript numbers -- JSConf EU 2013

Javascript gebruikt hiervoor een standaard die in meer programmeertalen gebruikt wordt (in ieder geval javascript). Ik weet niet of php exact hetzelfde gebruikt, maar het zal in ieder geval vergelijkbaar zijn.
Dit filmpje ga ik bekijken! Ook al is het in PHP misschien niet hetzelfde, het kan in ieder geval veel verhelderen!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
Hurm schreef op dinsdag 29 oktober 2013 @ 12:24:
[...]


Ik snap dat PHP gebruik maakt van floating points. Wat ik echter niet snap is waarom ik tegen die grens aan loop, zelfs als ik de precision verhoog.
Zie de handleiding: http://www.php.net/manual/en/ini.core.php#ini.precision

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Probeer het anders eens met http://php.net/BCMath

Maak je niet druk, dat doet de compressor maar


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Hurm schreef op dinsdag 29 oktober 2013 @ 12:18:
Wie kan mij uitleggen waarom ik tegen dit limiet aan loop
Kan jij me uitleggen waarom je werkt met getallen als 10-324? Heeft 't ook een daadwerkelijk praktisch nut of ben je gewoon aan 't kijken hoe ver je kunt gaan?

Je beseft dat dit een shiiiiiiiiiiiiiiiitload aan nullen is achter de komma? Lees anders onze Getallen en talstelsels FAQ eens (hfdst 13/14 bijv.).

[ Voor 11% gewijzigd door RobIII op 29-10-2013 12:38 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Hurm
  • Registratie: Maart 2010
  • Laatst online: 21-12-2014
RobIII schreef op dinsdag 29 oktober 2013 @ 12:37:
[...]

Kan jij me uitleggen waarom je werkt met getallen als 10-324? Heeft 't ook een daadwerkelijk praktisch nut of ben je gewoon aan 't kijken hoe ver je kunt gaan?

Je beseft dat dit een shiiiiiiiiiiiiiiiitload aan nullen is achter de komma? Lees anders onze Getallen en talstelsels FAQ eens (hfdst 13/14 bijv.).
Ik besef zeker dat dit een 'shitload' aan nullen achter de komma geeft. Ik besef ook dat dit uiteindelijk de precisie niet ten goede komt.

Blijft de vraag open, waarom PHP na 1075 keer het resultaat delen door 2 een 0 resulteert. Puur en alleen om het feit dat dit niet meer in het 64bits stelsel past waarmee PHP rekent?

Het praktisch nut van wat ik wil is er zeker. Ik wil namelijk numerieke stabiliteit binnen mijn berekeningen. Een triviaal geval, maar wel verduidelijkend, je mag maar met 3 getallen rekenen en je hebt het getal 1234. Dit zal resulteren in 123*101. Wanneer je vervolgens er een miljoen keer 1 bij op moet tellen, zal het resultaat alsnog 123*101 zijn, sinds 123*101 + 1 = 1234, maar in dit geval 123*101.

[ Voor 20% gewijzigd door Hurm op 29-10-2013 12:42 ]


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Hurm schreef op dinsdag 29 oktober 2013 @ 12:40:
Blijft de vraag open, waarom PHP na 1075 keer het resultaat delen door 2 een 0 resulteert.
Verander je code eens naar:
PHP:
1
2
3
4
5
6
7
8
9
$i = 1;
$counter = 0;

while($i != 0) {
  $i = $i/2;
  echo number_format($i,350) . "<br>";
  $counter++;
}
echo $counter;


Wat had je dan verwacht? (En, zoals ik zei, lees onze FAQ er eens op na ;) Of kijk even deze video vanaf een minuut of 6:00 ).

[ Voor 24% gewijzigd door RobIII op 29-10-2013 13:14 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 21-11 21:44
Hurm schreef op dinsdag 29 oktober 2013 @ 12:40:
[...]

Blijft de vraag open, waarom PHP na 1075 keer het resultaat delen door 2 een 0 resulteert. Puur en alleen om het feit dat dit niet meer in het 64bits stelsel past waarmee PHP rekent?
Tja hoe hoger de precisie, hoe meer ruimte zo'n getal inneemt bijvoorbeeld in je RAM geheugen. Aangezien PHP toch voornamelijk een taal is voor webtoepassingen en dus vaak op webservers draait, kan ik me voorstellen dat er een 'trade off' gemaakt is tussen precisie en prestaties. Je wilt voorkomen dat de RAM van een webserver snel volloopt.

Verder zal het voor de gemiddelde webtoepassing goed genoeg zijn. Zaken die zulke hoge precisie vereisen kun je beter op custom made systemen laten draaien lijkt me.

  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Waarom dan ook in PHP? Waarom geen Matlab of iets dergelijks?

Daarnaast, als je verwacht dat je in de praktijk oneindige precisie en ruimte hebt, dan kom je van een koude kermis thuis.

  • Hurm
  • Registratie: Maart 2010
  • Laatst online: 21-12-2014
Java, Python en Ruby maken ook gebruik van IEEE 754 double precision format, waardoor dezelfde fenomenen daar ook optreden.

Graag wil ik weg blijven van de discussie 'waarom maak je dan ook gebruik van x of y'. Het is in mijn ogen veel interessanter waarom iets dergelijks optreedt.

Het filmpje wat kaesve aangaf, legt het uit waarom het zo is. Dus ik denk dat mijn vraag beantwoord is door kaesve. Gegevens de 64 bits (eigenlijk 63 bits, aangezien de sign value eraf gaat) floating point, wordt de significand dermate klein dat dit binnen deze 63 bits niet meer aan te geven is. Je loopt dan ook tegen het limiet aan van de IEEE 754 double precision format.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:12

.oisyn

Moderator Devschuur®

Demotivational Speaker

Hurm schreef op dinsdag 29 oktober 2013 @ 12:40:
Blijft de vraag open, waarom PHP na 1075 keer het resultaat delen door 2 een 0 resulteert. Puur en alleen om het feit dat dit niet meer in het 64bits stelsel past waarmee PHP rekent?
Een double heeft een 53 bits mantissa en een 11 bits exponent. Een exponenten met allemaal nullen en allemaal énen hebben een speciale betekenis, dus hij gaat van -1022 tot 1023. Dit verklaart dus waarom je iig 1022 keer door 2 kunt delen. Deel je nog een keer (1023), dan krijg je zogenaamde "denormal numbers" waarbij er allemaal nullen in de exponent staan en de mantissa niet meer begint met een expliciete "1.". Je gaat daarna de bits in de mantissa depleten, en dat kun je 52 keer doen. Kom je uit op 1075.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Het praktisch nut van wat ik wil is er zeker. Ik wil namelijk numerieke stabiliteit binnen mijn berekeningen.
Ik heb je het antwoord hierboven al gegeven: BCMath
Natuurlijk zijn floats niet stabiel dus ga daar niet op vertrouwen.
Als het gaat om valuta dan zijn floats al helemaal uit den boze.

Daarnaast zijn er bij valuta ook nog tig manieren van afronden.
Je kan namelijk niet via ideal 9.995 afrekenen.

[ Voor 19% gewijzigd door DJMaze op 29-10-2013 12:59 ]

Maak je niet druk, dat doet de compressor maar


  • Hurm
  • Registratie: Maart 2010
  • Laatst online: 21-12-2014
.oisyn schreef op dinsdag 29 oktober 2013 @ 12:54:
[...]

Een double heeft een 53 bits mantissa en een 11 bits exponent. Een exponenten met allemaal nullen en allemaal hebben een speciale betekenis, dus hij gaat van -1022 tot 1023. Dit verklaart dus waarom je iig 1022 keer door 2 kunt delen. Deel je nog een keer (1023), dan krijg je zogenaamde "denormal numbers" waarbij er allemaal nullen in de exponent staan en de mantissa niet meer begint met een expliciete "1.". Je gaat daarna de bits in de mantissa depleten, en dat kun je 52 keer doen. Kom je uit op 1075.
Bedankt!!! _/-\o_

Dit is het antwoord wat ik zocht. Ik ben je héél erg dankbaar :) Weer een stukje praktijk opgelost met kei harde theorie.

Nu snap ik ook waarom 0.1+0.2 != 0.3 in Ruby, Python, Java en PHP. Aangezien 0.1 niet precies berekend kan worden, sinds dit getal berekent wordt met een negatieve macht. http://www.exploringbinar...-exist-in-floating-point/

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:12

.oisyn

Moderator Devschuur®

Demotivational Speaker

DJMaze schreef op dinsdag 29 oktober 2013 @ 12:56:
[...]

Natuurlijk zijn floats niet stabiel dus ga daar niet op vertrouwen.
Floats zijn weldegelijk stabiel, ze zijn alleen niet oneindig precies waardoor je in de knoei komt als de getallen waarmee je werkt onderling heel erg verschillen in orde van grootte. Je kunt met floats prima 1.05234234e50 bij een 6.235623534e51 optellen, of een 9.26234234e-435 bij een 2.6234123415e-433. Het probleem ontstaat echter wanneer je 1.05234234e50 bij 2.6234123415e-433 optelt - je zult dan geen verschil meten. Vaak is dat ook onbelangrijk. Numerieke stabiliteit hangt meestal vooral af van het algoritme en niet zozeer van het gebruikte datatype. Door bovenstaand euvel moet je goed nadenken over de volgorde van de operaties.
Als het gaat om valuta dan zijn floats al helemaal uit den boze.
Voor valuta wil je inderdaad een fixed point getal, niet een floating point getal. Je kunt je vergrijpen aan iets als bcmath, maar met een int kun je in feite prima rekenen met bedragen tot 42 miljoen, of kun je een float gebruiken om tot bedragen van 90 biljoen te gaan (door het bedrag in centen in de float te zetten en dus geen gebruik te maken van de komma). Beide bedragen zijn meer dan genoeg voor de typische webshop ;)

[ Voor 13% gewijzigd door .oisyn op 29-10-2013 13:06 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 23-10 08:50
Code van mn collega als testje:

Java:
1
2
3
4
5
6
7
float a = 0.1F;
float b = 0.2F;
float c = a + b; // 0.3

double d = 0.1;
double e = 0.2;
double f = d + e; // 0.30000000000000004

[ Voor 25% gewijzigd door Gamebuster op 29-10-2013 13:13 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:12

.oisyn

Moderator Devschuur®

Demotivational Speaker

Wat ging hij testen, de precisie van de binaire naar decimale conversie van resp. floats en doubles? Ik hoop niet dat hij nu de conclusie heeft getrokken dat floats preciezer zijn.

[ Voor 28% gewijzigd door .oisyn op 29-10-2013 13:09 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Gamebuster
  • Registratie: Juli 2007
  • Laatst online: 23-10 08:50
.oisyn schreef op dinsdag 29 oktober 2013 @ 13:09:
Ik hoop niet dat hij nu de conclusie heeft getrokken dat floats preciezer zijn.
Hoezo niet :o Dat zie je toch, bij floats heb je netjes 0.3 :+

Ging er meer om dat ik het zei dat je in Ruby dus false krijgt bij 0.1+0.2==0.3 en hij wilde kijken of het ook zo in Java zou zijn en of er verschil zou zijn tussen double en float.

[ Voor 17% gewijzigd door Gamebuster op 29-10-2013 13:14 ]

Let op: Mijn post bevat meningen, aannames of onwaarheden


  • HMS
  • Registratie: Januari 2004
  • Laatst online: 17-11 00:33

HMS

Hurm schreef op dinsdag 29 oktober 2013 @ 12:52:
Graag wil ik weg blijven van de discussie 'waarom maak je dan ook gebruik van x of y'. Het is in mijn ogen veel interessanter waarom iets dergelijks optreedt.
Ah, dit had ik zo niet begrepen uit je post. Eerder dat je opzoek was naar een oplossing om de precisie te verhogen. Excuus! :)

  • Hydra
  • Registratie: September 2000
  • Laatst online: 06-10 13:59
Ik denk ook dat mensen een typische google vraag "how do floating points work" gewoon niet verwachten omdat hier normaliter specifieke vragen staan.

https://niels.nu


  • NMe
  • Registratie: Februari 2004
  • Laatst online: 20-11 11:59

NMe

Quia Ego Sic Dico.

Hurm schreef op dinsdag 29 oktober 2013 @ 12:59:
[...]

Nu snap ik ook waarom 0.1+0.2 != 0.3 in Ruby, Python, Java en PHP. Aangezien 0.1 niet precies berekend kan worden, sinds dit getal berekent wordt met een negatieve macht. http://www.exploringbinar...-exist-in-floating-point/
Niet om lullig te doen, maar dat stond ook gewoon in de FAQ die RobIII hierboven al twee keer linkte: Programming FAQ - Getallen en talstelsels ;)

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


  • DJMaze
  • Registratie: Juni 2002
  • Niet online
.oisyn schreef op dinsdag 29 oktober 2013 @ 13:02:
Voor valuta wil je inderdaad een fixed point getal, niet een floating point getal. Je kunt je vergrijpen aan iets als bcmath, maar met een int kun je in feite prima rekenen met bedragen tot 42 miljoen, of kun je een float gebruiken om tot bedragen van 90 biljoen te gaan (door het bedrag in centen in de float te zetten en dus geen gebruik te maken van de komma). Beide bedragen zijn meer dan genoeg voor de typische webshop ;)
Niet echt als je met BTW percentages en kortingen gaat werken.
Je komt dan al snel op halve centen en dan gaat het al weer mis 8)7

Maak je niet druk, dat doet de compressor maar


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:12

.oisyn

Moderator Devschuur®

Demotivational Speaker

Euhm daar heb je met BCMath toch net zo goed last van? Gewoon een kwestie van tussentijds afronden.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Hydra
  • Registratie: September 2000
  • Laatst online: 06-10 13:59
DJMaze schreef op dinsdag 29 oktober 2013 @ 17:22:
[...]

Niet echt als je met BTW percentages en kortingen gaat werken.
Je komt dan al snel op halve centen en dan gaat het al weer mis 8)7
Als je met cent-delen moet werken betekent dit niet dat je niet met fixed-point getallen kan werken maar gewoon die point op een andere plek moet fixen :)

https://niels.nu


  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
Voor de compleetheid: de php.ini precision bepaalt hoeveel getallen achter de komma er geprint worden:

PHP:
1
2
3
4
5
<?php
$a = 0.1;
echo $a.PHP_EOL;
ini_set('precision', 50);
echo $a.PHP_EOL;

[ Voor 2% gewijzigd door ReenL op 29-10-2013 19:21 . Reden: php tags ]


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Hurm schreef op dinsdag 29 oktober 2013 @ 12:40:
Het praktisch nut van wat ik wil is er zeker. Ik wil namelijk numerieke stabiliteit binnen mijn berekeningen.
Numerieke stabiliteit is een ontzettend lastig onderwerp (een van de moeilijkste vakken vroeger bij ons). Als je niet precies weet waarom je oneindig vaak door 2 kan delen zou ik me daar verder niet al te druk over maken.
Pagina: 1