Ankündigung

Einklappen
Keine Ankündigung bisher.

PHP kann kein Mathe! ;)

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • PHP kann kein Mathe! ;)

    Leute ich glaub ich raffs nichtmehr!

    FEHLER-SCRIPT:
    http://mitarbeiter.salzburg.seminar-...rack/kakke.php

    200 - 200 ist meiner Meinung nach 0, und nicht 1.70530256582E-13!

    Code:
    <pre>
    <?PHP
    
      // Scriptlocation:
      // http://mitarbeiter.salzburg.seminar-...rack/kakke.php
    
      for( $x = 4; $x > 2; $x-- ) {
        $a = 2.2;
        $b = floor(2.2);
        
        $intDoubleDetection = ($a - $b) * 1000;
        var_dump(
          $a,
          $b,
          $intDoubleDetection,
          floor($intDoubleDetection),
          (int)$intDoubleDetection,
          ($intDoubleDetection - (int)$intDoubleDetection)
        );
        
        exit();
        
      }
      
    ?>
    </pre>
    Wer weis wo der Fehler liegt?

    Vielen Dank im Voraus.

    Michael Rack

  • #2
    weißt du, wie der datentyp floor funktioniert? http://de.wikipedia.org/wiki/Gleitkommazahl
    lies dir das durch, dann verstehst du den grund.

    Kommentar


    • #3
      ist es nicht eigentlich der Prozessor der die Rechnung ausrechnet? und nicht die Programmiersprache ?!

      Kommentar


      • #4
        Ich versteh das trozdem nicht.. Gut ich verwende jetzt die bcsub funktion, jetzt funktioniert auch alles schön...

        Aber dann sag mir mal warum einfachste Mathematik nicht funktioniert:
        Code:
        <pre>
        <?PHP
        
          // Scriptlocation:
          // http://mitarbeiter.salzburg.seminar-...ack/kakke2.php
          
          $a = 200.01;
          $b = 200;
          $c = $a - $b;
          var_dump( $c );
          
        ?>
        </pre>
        Ausgabe = float(0.00999999999999)
        http://mitarbeiter.salzburg.seminar-...ack/kakke2.php

        Kommentar


        • #5
          das wurde doch auf wikipedia erklärt. eine floatzahl wird als
          m*2^e abgespeichert, wobei m und e ganzzahlen sind. Es gibt eben Zahlen, die sich so nicht exakt speichern lassen. wenn du das nicht willst, sach halt, dass du integer zahlen haben willst.

          Kommentar


          • #6
            $a = 2.2;
            $b = floor(2.2);

            $intDoubleDetection = ($a - $b) * 1000;
            settype($intDoubleDetection, "integer");



            Obiger Zusatz sollte (mit etwas Glück) die Näherungsprobleme umgehen. Besser ist natürlich eine sauberere Lösung mittels ordentlicher Funktionen (round und Konsorten) unter Verwendung der gewünschten Nachkomastellen.

            Kommentar


            • #7
              dieselben Probleme tauchen auch bei JavaScript auf oder in alten, höheren DOS-Programmiersprachen die einen 32-Bit-Rechner auf einem 8-Bit-System simulierten. Ursache war immer der falsche Variablentyp.

              wenn man sich mal mit einem Hex-Editor die Zahlen anzeigen lässt, findet man auch schnell heraus warum. Wenn man dan aus einer 8-Bit-Zahl (200) eine 64-Bit-Zahl macht (3.414E-13) dann kommt das richtige Ergebnis als 64-Bit-Zahl heraus.

              11001000-11001000 ist 00000000 also richtig 0
              setzt man vor die 11001000 eine undefinierte 56-Bit-Reihe und im Speicher wäre da noch was, schreibt er nicht in den Speicher hat aber den Platz reserviert und liest das aus, was noch da ist.

              nur zum Verständnis ein Erklärungsversuch -200-200 :

              minus 200 wäre dann als Byte 111000 oder 00111000 oder
              11111111111111111111111111111111111111111111111111 11111100111000
              wenn man von diesem 32-Bit-Code ein 8-Bit-Wert (int) mit dem Wert 200 also 00111000 abzieht käme dann
              11111111111111111111111111111111111111111111111111 11111100000000
              heraus. Das wäre dann 18446744073709551360 aber nicht -400 !

              nun mache das man mal mit 64-Bit und positiven Zahlen

              Kommentar


              • #8
                also nochmal ne ernste Frage, rechnet nicht der Prozessor?

                Es rechnet doch nicht die Programmiersprache..

                Kommentar


                • #9
                  @aberchen. Prinzipiell ist die Erklärung gut, aber in diesem Fall nicht zutreffend, da 1. PHP korrekt vorinitialisiert und b) das Problem nur bei Ganzzahligen Sachen auftreten, aber nicht bei Fliesskommazahlen.

                  @dsmcg. Letzlich rechnet immer der Prozessor. Wie kommt aber logischerweise auf die Programmiersprache an. C beispielsweise ist sehr prozessornah, weshalb eine Rechnung hier relativ transparent ist. Aber egal, PHP arbeitet hier auch relativ transparent. Es hat "nur" eine variable Zahlendrarstellung.
                  Das Hauptproblem ist immer, dass Fliesskommazahlen klassischerweise wie oben schon dargestellt in ein internes Format umgesetzt werden, dass nur Näherungen zulässt. Normalerweise wird dies berücksichtig, so dass eine gewisse Nachkommastellenzahl angenommen wird und dann entsprechend bei Ergebnissen gerundet wird. Problem ist, dass bei der Mulitplikation mit 1000 diese Näherung verlassen wird und damit die Rundung nicht mehr zuschlägt. Genau weiss ich es nicht, da auch die möglichen Näherungen variabel sind und ich nicht im Kopf habe, wie die Zend Engine effektiv arbeitet bzw. mit wieviel Bit die Fliesskommazahlen intern dargestellt werden. Ist auch egal.

                  Die Rechnung mit Ganzzahlen läuft anders, da die, wie aberchen schrieb, anders dargestellt werden und daher immer genau sind, solange die vorgesehene Bit-Breite nicht überschritten wird. Bei Divisionen wird eventuell ein Float aus einem Integer und damit gelten wieder die Näherungsprobleme.

                  bcsub arbeitet komplett anders, also mit anderen Zahlenrepräsentationen, was aber auch bedeutet, dass es die Näherungsprobleme nicht verhindert, nur soweit minimiert, dass sie bei "gängigen" Rechnungen nicht mehr auftreten. Aber Probleme haben die bc*- Funktionen ebenfalls. Zudem sind sie deutlich langsamer als die Prozessornahem Berechnungen.

                  Kommentar


                  • #10
                    Zitat von dsmcg
                    also nochmal ne ernste Frage, rechnet nicht der Prozessor?

                    Es rechnet doch nicht die Programmiersprache..
                    nee, eigentlich kann der Prozessor gar nicht rechnen, bzw. rechnet er anders, z.B. es wird ein Bit nach links verschoben, das kann +8 bedeuten oder mal 2.

                    1000 ist 8, eins nach links ist 10000 ist 16
                    2 Rechnungen ein Ergbnis

                    andere Rechnung:
                    1100 ist 12, eins nach links ist 10100 also 20
                    das ist 12+8 aber nicht 12*2

                    12*2 wäre 11000 also 2 mal um 1 nach links verschoben
                    natürlich hat die hardware heutiger Prozessoren Millionen von solchen Verschiebeoperatoren in Form von Flops fest eingebaut.
                    aber das Prinzip ist 1 +1 oder 1-1

                    Kommentar


                    • #11
                      Zitat von mepeisen
                      @aberchen. Prinzipiell ist die Erklärung gut, aber in diesem Fall nicht zutreffend, da 1. PHP korrekt vorinitialisiert
                      aber nicht wenn man mit PHP eine 64-Bit-Zahl definiert aber davon nur 8-Bit abzieht. Wer sich auf solche Aussagen verlässt kann dann gleich bei Microsoft anfangen.



                      das war nämlich genau der grosse Irrtum bei Bill Gates Basic.
                      Man muss immer Variablen korrekt definieren, wenn man genau rechnen möchte.

                      Kommentar


                      • #12
                        Ich kenne die Zend-Engine etwas besser, da ich mich damit schonmal auseinandergesetzt habe.

                        PHP kennt nur eine interne Repräsentation der Integers und das sind 32 Bit bzw. long (kann auch mal 64 bit sein). Und diese werden immer korrekt vorinitialisiert. Da auf gängigen Systemen (32 Bit Systemen) das long im GCC einer 32Bit-Zahl entspricht, liegt das problem nicht bei deem Abzug mit der 8Bit-Zahl (sowas kennt PHP gar nicht, denn es arbeitet grundsätzlich mit 32 Bit), sondern daran, dass die erste Zahl (64 Bit) bereits nicht intern korrekt verrechnet werden kann, wenn sie nicht als Float dargestellt wird...

                        Sorry, Aberchen, aber so richtig wie dein Beispiel sein kann, so falsch ist es in Bezug auf PHP und vor allem in Bezug auf den dargelegten Fehler

                        Inwieweit bei einem 64-bit System, wo das long 64 Bit sein kann, alles korrekt vorinitialisiert wird, weiss ich nicht, da es aber keinen Bug diesbezüglich gibt und Zend seit längerem auch 64Bit-Compiles unterstützt, gehe ich davon aus, dass auch hier alles korrekt läuft.

                        Kommentar


                        • #13
                          Zitat von mepeisen
                          Ich kenne die Zend-Engine etwas besser, da ich mich damit schonmal auseinandergesetzt habe.

                          PHP kennt nur eine interne Repräsentation der Integers und das sind 32 Bit bzw. long (kann auch mal 64 bit sein). Und diese werden immer korrekt vorinitialisiert. Da auf gängigen Systemen (32 Bit Systemen) das long im GCC einer 32Bit-Zahl entspricht, liegt das problem nicht bei deem Abzug mit der 8Bit-Zahl (sowas kennt PHP gar nicht, denn es arbeitet grundsätzlich mit 32 Bit), sondern daran, dass die erste Zahl (64 Bit) bereits nicht intern korrekt verrechnet werden kann, wenn sie nicht als Float dargestellt wird...

                          Sorry, Aberchen, aber so richtig wie dein Beispiel sein kann, so falsch ist es in Bezug auf PHP und vor allem in Bezug auf den dargelegten Fehler

                          Inwieweit bei einem 64-bit System, wo das long 64 Bit sein kann, alles korrekt vorinitialisiert wird, weiss ich nicht, da es aber keinen Bug diesbezüglich gibt und Zend seit längerem auch 64Bit-Compiles unterstützt, gehe ich davon aus, dass auch hier alles korrekt läuft.
                          Hallo,

                          Du verwechselst da was... mit 32 Bit kann man keine 3.414E-13 korrekt darstellen. Die Wortlänge ist 64 Bit oder auch als Qword bezeichnet, eine Optimierung wäre dann nur logich wenn 8 Bit auch nur 1 Byte beanspruchen und nicht 4 Bytes (32 Bit).
                          Reserviert man aber 4 Bytes mittels float oder gar 8 Bytes muss man von 4 Bytes auch 4 Bytes abziehen und nicht nur 1 Byte.
                          Berechne mal was eine normale for-Schleife dann an Speicherplatz und Zeit beanspruchen würde, wenn die Variable $i 32Bit Wortlänge anstatt 8Bit hätte.
                          Ich rede nicht von der Busbreite 32-Bit oder Assemblerbefehlskette von 32-Bit, sondern von der binären Wortlänge, auch als Qword, Dword, word oder byte bezeichnet. In einem 32-Bitbus werden z.B. 4 x 8-Bit- bzw. Int-Zahlen an die CPU übergeben, bei 32-Bit-Wortlänge für Integer-8-Bit-Zahlen müsste man 4mal langsamer arbeiten.

                          Kommentar


                          • #14
                            PHP-Code:
                            <?php


                              
                            // Scriptlocation:
                              // [url]http://mitarbeiter.salzburg.seminar-shop.com/rack/kakke.php[/url]

                              
                            for( $x 4$x 2$x-- ) {
                                
                            $a 2.2//= float! 32Bit- Wortlänge
                                
                            $b floor(2.2); //=int 8-Bit-Wortlänge 

                                
                            $intDoubleDetection = (int)(($a - (float)$b) * 1000);
                                echo 
                            $intDoubleDetection;
                                
                            var_dump(
                                  
                            $a,
                                  
                            $b,
                                  
                            $intDoubleDetection,
                                  
                            floor($intDoubleDetection),
                                  (int)
                            $intDoubleDetection,
                                  (
                            $intDoubleDetection - (int)$intDoubleDetection)
                                );

                                exit();

                              }


                            ?>
                            und hier die Lösung !

                            und nochmal eine demo:
                            PHP-Code:
                            <?PHP

                              
                            // Scriptlocation:
                              // [url]http://mitarbeiter.salzburg.seminar-shop.com/rack/kakke.php[/url]

                              
                            for( $x 4$x 2$x-- ) {
                                
                            $a floor(2.2);// immer noch int
                                
                            $b 2.2;//immer noch float

                                
                            $intDoubleDetection = (($a -$b) * -1000);// auch float
                               
                            echo "Richtig=(int)((int)$intDoubleDetection-(float)$intDoubleDetection)=";
                            echo (int)((int)
                            $intDoubleDetection-(float)$intDoubleDetection);
                            echo 
                            "
                            \n"
                            ;

                            echo
                            "Fehler=(int)$intDoubleDetection-(float)$intDoubleDetection=";
                            echo (int)
                            $intDoubleDetection-(float)$intDoubleDetection;


                            echo 
                            "
                            \n"
                            ;
                                
                            var_dump(
                                  
                            $a,
                                  
                            $b,
                                  
                            $intDoubleDetection,
                                  
                            floor($intDoubleDetection),
                                  (int)
                            $intDoubleDetection,
                                  (
                            $intDoubleDetection - (int)$intDoubleDetection)
                                );

                                exit();

                              }

                            ?>

                            Kommentar


                            • #15
                              Zitat von aberchen
                              mit 32 Bit kann man keine 3.414E-13
                              Richtig, weil das gar keine Integer-Zahl ist, sondern eine Fliesskommazahl und dazu eine sehr sehr sehr kleine. Ausgeschrieben: 0,0000000000003414. Daher gilt das, was du oben beschrieben hast, für diese Zahl sowieso nicht, auch wenn sachlich alles richtig ist bzw. das Problem durchaus existiert, spielt es hierfür keinerlei Rolle.

                              Zitat von aberchen
                              Die Wortlänge ist 64 Bit oder auch als Qword bezeichnet, eine Optimierung wäre dann nur logich wenn 8 Bit auch nur 1 Byte beanspruchen und nicht 4 Bytes (32 Bit).
                              Nochmal: Das hat insofern mit PHP nix zu tun, als PHP grundsätzlich intern mit 32-Bit-Zahlen (bei den Integers) arbeitet und nicht mit 64Bit, zumindest auf einem heute üblichen 32-Bit-System. Genauer arbeitet es dann mit 64Bit, wenn es für ein 64-Bit-System compiliert wurde (wo long entsprechend 64 Bit belegt), dann aber ausschliesslich mit 64Bit. Die Zahl "16" belegt immer in PHP 32Bit (mit Meta-Infos sogar noch mehr), die zuverlässig vorinitialisiert werden, so dass das Problem so nie auftreten kann. siehe auch http://www.php.net/integer . Zend kennt keine 8Bit-Zahlen.
                              Zitat von aberchen
                              Reserviert man aber 4 Bytes mittels float oder gar 8 Bytes muss man von 4 Bytes auch 4 Bytes abziehen und nicht nur 1 Byte.
                              Richtig, was aber für PHP völlig irrelevant ist, weil es nunmal keine 8Bit-Zahlen kennt, sondern ausschliesslich 32Bit (bzw. 64Bit). Gemischte Zahlenbreiten kommen in PHP intern nie vor.
                              Zitat von aberchen
                              Berechne mal was eine normale for-Schleife dann an Speicherplatz und Zeit beanspruchen würde, wenn die Variable $i 32Bit Wortlänge anstatt 8Bit hätte.
                              Ich rede nicht von der Busbreite 32-Bit oder Assemblerbefehlskette von 32-Bit, sondern von der binären Wortlänge, auch als Qword, Dword, word oder byte bezeichnet.
                              OK, nun hast mich gedanklich abgehängt, weil ich den Zusammenhang nicht sehe zum Problem. Nochmal: Deine Erklärungen waren bis hier richtig, auch wenn sie mit dem Problem nix zu tun haben. Dein neues Szenario verstehe ich nicht. Aber egal.
                              Auszug aus Quellcode von php5.1.2, zend.h (Metadaten bleiben mal unberücksichtigt)
                              Code:
                              typedef union _zvalue_value {
                              	long lval;					/* long value */
                              	double dval;				/* double value */
                              	struct {
                              		char *val;
                              		int len;
                              	} str;
                              	HashTable *ht;				/* hash table value */
                              	zend_object_value obj;
                              } zvalue_value;
                              Macht bei mir immer 32 Bit für ein Integer. Andere Darstellung kann PHP gar nicht verarbeiten, es arbeitet grundsätzlich mit 32Bit. Insgesamt belegt das Ding ($i) also ohne Metadaten immer 64Bit (zend_object_value sind 2mal 32Bit) und auf 64Bit-Systemen sogar 128Bit. Verarbeitet werden davon nur 32Bit. Da der Prozessor logischerweise auch nur 32Bit verarbeitet bei den entsprechenden Berechnungen, stellt sich dein Problem nie.
                              Zitat von aberchen
                              In einem 32-Bitbus werden z.B. 4 x 8-Bit- bzw. Int-Zahlen an die CPU übergeben, bei 32-Bit-Wortlänge für Integer-8-Bit-Zahlen müsste man 4mal langsamer arbeiten.
                              siehe auch: http://www.intel.com/design/intarch/...oc.htm#Manuals
                              Direktlink: ftp://download.intel.com/design/Pent...s/24896612.pdf
                              Seite 71. FÜr Details die anderen Dokumente durchgucken. Das, was du schreibst ist nicht unbedingt falsch, aber für die Intel-Reihe seit Pentium (also seit 32Bit) definitiv falsch. Denn so arbeitet der Prozessor nicht. Ich behaupte sogar ungeprüft, dass kein einziger 32Bit-Prozessor so arbeitet, da es höchst ineffizient wäre, von einem 32Bit Adressbus nur 8 Bit zu benutzen und das 4mal, wenn man die 32Bit auch mit nur einem Zyklus verwenden könnte. Das macht einfach keinen Sinn.

                              Kommentar

                              Lädt...
                              X