Peter van Panski: Float-Zahl kürzen (74,34324148823 ==> 74,34)

Guten Morgen,

ich muss cm ins inch umwandeln (kein problem), aber es entsteht ein Problem dabei, die Inch-Zahl ist ca. 10 Stellig nach dem Komma. Ich braucher aber 2 Stellen vor, und 2 Stellen nach dem Komma.
Wie kürze ich diese Zahl, mit Verbindung von JavaScript. Die cm-Zahl ist dynamisch, bewegt sich zwischen 160cm und 190cm.

Ich bitte um Hilfe

  1. @@Peter van Panski:

    […] 2 Stellen nach dem Komma.
    Wie kürze ich diese Zahl, mit Verbindung von JavaScript.

    Nur zur Ausgabe oder zum Weiterrechnen?

    Zur Ausgabe: http://de.selfhtml.org/javascript/objekte/number.htm#to_fixed@title=toFixed()

    Zum Weiterrechnen: Mit 100 multipilizieren, http://de.selfhtml.org/javascript/objekte/math.htm#round@title=runden, durch 100 teilen.

    Live long and prosper,
    Gunnar

    --
    Erwebsregel 208: Manchmal ist das einzige, was gefährlicher als eine Frage ist, eine Antwort.
    1. Hallo Gunnar,

      […] 2 Stellen nach dem Komma.
      Wie kürze ich diese Zahl, mit Verbindung von JavaScript.

      Nur zur Ausgabe oder zum Weiterrechnen?

      Zur Ausgabe: http://de.selfhtml.org/javascript/objekte/number.htm#to_fixed@title=toFixed()

      Der Algorithmus macht aber nicht 100% das, was man erwartet, siehe z.B. javascript:alert(1.255.toFixed(2)); (sollte sowohl in banker's rounding als auch in arithmetischem Runden 1.26 geben)

      Zum Weiterrechnen: Mit 100 multipilizieren, http://de.selfhtml.org/javascript/objekte/math.htm#round@title=runden, durch 100 teilen.

      Der hier auch nicht (macht im Prinzip das gleiche wie toFixed).

      Siehe auch mein anderes Posting.

      Viele Grüße,
      Christian

      1. Der Algorithmus macht aber nicht 100% das, was man erwartet, siehe z.B. javascript:alert(1.255.toFixed(2)); (sollte sowohl in banker's rounding als auch in arithmetischem Runden 1.26 geben)

        So wie ich es gelernt habe, hat man bei 1.255 die Möglichkeit AUF, oder AB zurunden, spricht auf 1.25 oder auf 1.26.
        Bei 1,254 wird abgerudet, ab 1,256 aufgerundet, ist genau das, was ich gesucht habe.

        1. Der Algorithmus macht aber nicht 100% das, was man erwartet, siehe z.B. javascript:alert(1.255.toFixed(2)); (sollte sowohl in banker's rounding als auch in arithmetischem Runden 1.26 geben)
          So wie ich es gelernt habe, hat man bei 1.255 die Möglichkeit AUF, oder AB zurunden, spricht auf 1.25 oder auf 1.26.

          Wo hast du das gelernt? bisher habe ich immer gehört, dass eine 5 immer aufgerundet wird.

          Struppi.

          1. @@Struppi:

            So wie ich es gelernt habe, hat man bei 1.255 die Möglichkeit AUF, oder AB zurunden, spricht auf 1.25 oder auf 1.26.

            Nein. Die Rundungsregeln lassen kein Ermessensspielraum. Es ist genau festgelegt, wann auf- und wann abgerundet wird. Kein Oder.

            Wo hast du das gelernt? bisher habe ich immer gehört, dass eine 5 immer aufgerundet wird.

            Wo hast du das gelernt? Bei BWLern? Die Gerade-Zahl-Regel ist dir kein Begriff?

            Live long and prosper,
            Gunnar

            --
            Erwebsregel 208: Manchmal ist das einzige, was gefährlicher als eine Frage ist, eine Antwort.
            1. Wo hast du das gelernt? bisher habe ich immer gehört, dass eine 5 immer aufgerundet wird.

              Wo hast du das gelernt? Bei BWLern? Die Gerade-Zahl-Regel ist dir kein Begriff?

              Noch nie gehört.

              Struppi.

            2. Wo hast du das gelernt? Bei BWLern? Die Gerade-Zahl-Regel ist dir kein Begriff?

              Bei mir selbst habe ich das gelernt ;)
              Da 0,5 die mitte ist, hab ich die wahl!
              Ist das Glas halb voll, oder halb leer? :)

              1. Wo hast du das gelernt? Bei BWLern? Die Gerade-Zahl-Regel ist dir kein Begriff?
                Bei mir selbst habe ich das gelernt ;)

                Zitat: Diese Art der Rundung ist bei Logarithmentafeln und im Vermessungswesen üblich.

                Struppi.

            3. Hallo Gunnar,

              So wie ich es gelernt habe, hat man bei 1.255 die Möglichkeit AUF, oder AB zurunden, spricht auf 1.25 oder auf 1.26.

              Nein. Die Rundungsregeln lassen kein Ermessensspielraum. Es ist genau festgelegt, wann auf- und wann abgerundet wird. Kein Oder.

              Es gibt aber verschiedene Rundungsregeln. round-to-negative-infinity wäre zum Beispiel immer richtung -unendlich runden, das macht floor(), round-to-positive-infinity wäre ceil(), es gibt ferner noch round-to-zero (floor() für positive und ceil() für negative Zahlen) und round-away-from-zero (ceil() für positive, floor() für negative Zahlen). Dann gibt es noch round-to-nearest (d.h. zur nächsten Zahl runden), da gibt's aber typischerweise 4 Unterarten, wenn das Ende 0.5 ist:

              - round-half-up: .5 wird zur betragsmäßig nächstgrößeren Zahl gerundet
                 (2.5 zu 3, 3.5 zu 4, -2.5 zu -3, -3.5 zu -4)
                      Dies bezeichnet man auch als arithmetisches Runden und das ist auch
                      der Algorithmus, der fast immer in der Schule gelehrt wird.
               - round-half-down: .5 wird zur betragsmäßig nächstkleineren Zahl gerundet
                 (2.5 zu 2, 3.5 zu 3, -2.5 zu -2, -3.5 zu -3)
                      Wird so gut wie nie verwendet.
               - round-half-even: .5 wird zur nächsten geraden Zahl gerundet
                 (2.5 zu 2, 3.5 zu 4, -2.5 zu -2, -3.5 zu -4)
                      Nennt sich auch "banker's rounding" und ist in der Finanzwelt
                      verbreitet
               - round-half-odd: .5 wird zur nächsten ungeraden Zahl gerundet
                 (2.5 zu 3, 3.5 zu 3, -2.5 zu -3, -3.5 zu -3)
                      Wird so gut wie nie verwendet.

              Im Prinzip gibt's auch noch diese beiden, die werden aber wegen ihrer Asymmetrie eigentlich nie verwendet:

              - round-half-to-pos-infty: .5 wird zur Zahl näher an +unendlich gerundet
                 (2.5 zu 3, 3.5 zu 4, -2.5 zu -2, -3.5 zu -3)
               - round-half-to-neg-infty: .5 wird zur Zahl näher an -unendlich gerundet
                 (2.5 zu 2, 3.5 zu 3, -2.5 zu -3, -3.5 zu -4)

              Man kann sich im Prinzp aussuchen, welche Rundungsregel man haben möchte - dann sollte man es allerdings bei dieser Rundungsregel belassen. Das heißt: hat man sich einmal festgelegt, sollte man nicht mehr davon abweichen.

              Viele Grüße,
              Christian

              1. @@Christian Seiler:

                • round-half-up: .5 wird zur betragsmäßig nächstgrößeren Zahl gerundet
                     (2.5 zu 3, 3.5 zu 4, -2.5 zu -3, -3.5 zu -4)
                          Dies bezeichnet man auch als arithmetisches Runden und das ist auch
                          der Algorithmus, der fast immer in der Schule gelehrt wird.

                Ist das heute so? Wir haben damals in der allgemeinbildenden polytechnischen Oberschule die Rundungsregeln gelernt, die du als „round-half-even“ bezeichnest:

                • round-half-even: .5 wird zur nächsten geraden Zahl gerundet
                     (2.5 zu 2, 3.5 zu 4, -2.5 zu -2, -3.5 zu -4)
                          Nennt sich auch "banker's rounding" und ist in der Finanzwelt
                          verbreitet

                Da unterscheiden sich wohl der deutsche und der angelsächsische Sprachraum. Das kaufmännische Runden ist das, was du als „round-half-up“ bezeichnest.

                Live long and prosper,
                Gunnar

                --
                Erwebsregel 208: Manchmal ist das einzige, was gefährlicher als eine Frage ist, eine Antwort.
                1. Hallo Gunnar,

                  Ist das heute so? Wir haben damals in der allgemeinbildenden polytechnischen Oberschule die Rundungsregeln gelernt, die du als „round-half-even“ bezeichnest

                  Hmm, ich hab an 4 unterschiedlichen Schulen (in 4 unterschiedlichen Ländern) round-half-up beigebracht bekommen. Und alle Kommilitonen von mir kennen auch nur diese Regel.

                  Da unterscheiden sich wohl der deutsche und der angelsächsische Sprachraum. Das kaufmännische Runden ist das, was du als „round-half-up“ bezeichnest.

                  Offensichtlich... http://en.wikipedia.org/wiki/Rounding <- "arithmetic rounding" für round-half-up, "banker's rounding" für round-half-even.

                  Viele Grüße,
                  Christia

          2. Hi,

            So wie ich es gelernt habe, hat man bei 1.255 die Möglichkeit AUF, oder AB zurunden, spricht auf 1.25 oder auf 1.26.
            Wo hast du das gelernt? bisher habe ich immer gehört, dass eine 5 immer aufgerundet wird.

            so habe ich es in der Schule auch gelernt; von der Vielfalt der anderen im Thread erwähnten Verfahren, insbesondere den half-even-Versionen, habe ich teils noch nie gehört.

            Während des Studiums habe ich dann häufig vom Runden auf "halbe" Stellen gehört, für einige unserer Profs war das ganz selbstverständlich. Auf 1½ Nachkommastellen zu runden hieß, zwei Stellen nach dem Komma anzugeben, wobei die zweite nur die Werte 0 oder 5 hatte. Also z.B. 1.00, 1.05, 1.10, 1.15, 1.20 ...

            So long,
             Martin

            --
            Ich stehe eigentlich gern früh auf.
            Außer morgens.
            1. Hallo Martin,

              So wie ich es gelernt habe, hat man bei 1.255 die Möglichkeit AUF, oder AB zurunden, spricht auf 1.25 oder auf 1.26.
              Wo hast du das gelernt? bisher habe ich immer gehört, dass eine 5 immer aufgerundet wird.

              so habe ich es in der Schule auch gelernt; von der Vielfalt der anderen im Thread erwähnten Verfahren, insbesondere den half-even-Versionen, habe ich teils noch nie gehört.

              Während des Studiums habe ich dann häufig vom Runden auf "halbe" Stellen gehört, für einige unserer Profs war das ganz selbstverständlich. Auf 1½ Nachkommastellen zu runden hieß, zwei Stellen nach dem Komma anzugeben, wobei die zweite nur die Werte 0 oder 5 hatte. Also z.B. 1.00, 1.05, 1.10, 1.15, 1.20 ...

              Das ist aber unabhängig von der anderen Problematik, denn bei 1.175 in Deinem Beispiel wäre auch nicht offensichtlich, ob das zu 1.15 oder 1.20 gewertet werden sollte, hier kann man auch alle der verschiedenen Verfahren anwenden (genaugenommen ist der Algorithmus im Prinzip identisch zu den vorigen, nur, dass man mit 20 multizpliziert statt mit 100, dann rundet und dann wieder durch 20 dividiert.

              Viele Grüße,
              Christian

        2. Hallo,

          Der Algorithmus macht aber nicht 100% das, was man erwartet, siehe z.B. javascript:alert(1.255.toFixed(2)); (sollte sowohl in banker's rounding als auch in arithmetischem Runden 1.26 geben)
          So wie ich es gelernt habe, hat man bei 1.255 die Möglichkeit AUF, oder AB zurunden, spricht auf 1.25 oder auf 1.26.

          Wo hast Du denn das bitteschön her? Beim Runden einigt man sich auf eine bestimmte Konvention (siehe mein anderes Posting für die verschiedenen Konventionen, die es gibt). Es gibt Konventionen, bei denen 1.255 abgerundet wird, aber die werden so gut wie nie (!) verwendet. Beide (!) gebräuchlichen Konventionen (arithmetisches Runden, banker's rounding) schreiben vor, dass 1.255 auf 2 Stellen aufgerundet werden muss zu 1.26. Und gerade deswegen ist 1.255.toFixed(2) eben nicht das erwartete Ergebnis (hängt mit der Darstellung von floats im Computer zusammen, siehe meinen verlinkten PHP-Vorschlag für eine genauere Erklärung).

          Viele Grüße,
          Christian

  2. Hallo,

    ich muss cm ins inch umwandeln (kein problem), aber es entsteht ein Problem dabei, die Inch-Zahl ist ca. 10 Stellig nach dem Komma. Ich braucher aber 2 Stellen vor, und 2 Stellen nach dem Komma.
    Wie kürze ich diese Zahl, mit Verbindung von JavaScript. Die cm-Zahl ist dynamisch, bewegt sich zwischen 160cm und 190cm.

    Wenn Dir kleinere Probleme beim Runden egal sind (es gibt bei Float-Zahlen prinzipiell einige Dinge, die anders reagieren, als bei Dezimalzahlen - und das irgendwie "hinzubiegen" ist alles andere als trivial), dann kannst Du das Math-Objekt um eine Funktion erweitern:

    Math.roundToPlaces = function (number, places) {  
     var p = Math.pow(10, places);  
     var f = number * p;  
     if (f >= 0.0) {  
      f  = Math.floor(f + 0.5);  
     } else {  
      f = Math.ceil(f - 0.5);  
     }  
     f = f / p;  
     if (isFinite(f)) {  
      return f;  
     } else {  
      return number;  
     }  
    };
    

    Das rundet Dir arithmetisch, d.h. 0.5 immer zur nächsthöheren Zahl. Du kannst aber z.B. auch Banker's Rounding verwenden, dann nutze Math.round():

    Math.roundToPlaces = function (number, places) {  
     var p = Math.pow(10, places);  
     var f = number * p;  
     f = Math.round(f);  
     f = f / p;  
     if (isFinite(f)) {  
      return f;  
     } else {  
      return number;  
     }  
    };
    

    Auf Grund der oben erwähnten Probleme funktioniert das aber nicht immer genau so, wie erwartet:

    Math.roundToPlaces(1.255, 2)   -> gibt 1.25 obwohl man mit *beiden*
                                      Verfahren

    Ich habe mich vor einiger Zeit mal mit dem Thema "Runden" intensiver auseinandergesetzt und ein RFC geschrieben, wie PHPs round()-Funktion verbessert werden kann (die bisherige Implementierung entspricht meiner obigen). Mein Vorschlag ist hier zu sehen: http://wiki.php.net/rfc/rounding, dort auch mit allen möglichen teils komplizierten Erklärungen.

    Ich habe den Algorithmus mal nach Javascript portiert (ist nicht besonders effizient, funktioniert aber):

    (function () {  
     Math.ROUND_MODE_HALF_UP = 1;  
     Math.ROUND_MODE_HALF_DOWN = 2;  
     Math.ROUND_MODE_HALF_EVEN = 3;  
     Math.ROUND_MODE_HALF_ODD = 4;  
      
     var roundHelper = function (value, mode) {  
      var tmp_value;  
      if (value >= 0.0) {  
       tmp_value = Math.floor(value + 0.5);  
       if ((mode == Math.ROUND_MODE_HALF_DOWN && value == (-0.5 + tmp_value)) ||  
        (mode == Math.ROUND_MODE_HALF_EVEN && value == (0.5 + 2 * Math.floor(tmp_value/2.0))) ||  
        (mode == Math.ROUND_MODE_HALF_ODD && value == (0.5 + 2 * Math.floor(tmp_value/2.0) - 1.0)))  
       {  
        tmp_value -= 1.0;  
       }  
      } else {  
       tmp_value = Math.ceil(value - 0.5);  
       if ((mode == Math.ROUND_MODE_HALF_DOWN && value == (0.5 + tmp_value)) ||  
        (mode == Math.ROUND_MODE_HALF_EVEN && value == (-0.5 + 2 * Math.ceil(tmp_value/2.0))) ||  
        (mode == Math.ROUND_MODE_HALF_ODD && value == (-0.5 + 2 * Math.ceil(tmp_value/2.0) + 1.0)))  
       {  
        tmp_value += 1.0;  
       }  
      }  
      return tmp_value;  
     };  
      
     Math.roundToPlaces = function (value, places, mode) {  
      if (typeof(places) == 'undefined') places = 0;  
      if (typeof(mode) == 'undefined') mode = Math.ROUND_MODE_HALF_UP;  
      
      var f1, f2;  
      var precision_places = 14 - Math.floor(Math.log(Math.abs(value))/Math.LN10);  
      
      f1 = Math.pow(10.0, Math.abs(places));  
      
      if (precision_places > places && precision_places - places < 15) {  
       f2 = Math.pow(10.0, Math.abs(precision_places));  
       if (precision_places >= 0) {  
        tmp_value = value * f2;  
       } else {  
        tmp_value = value / f2;  
       }  
       tmp_value = roundHelper(tmp_value, mode);  
       f2 = Math.pow(10.0, Math.abs(places - precision_places));  
       // places < precision_places  
       tmp_value = tmp_value / f2;  
      } else {  
       if (places >= 0) {  
        tmp_value = value * f1;  
       } else {  
        tmp_value = value / f1;  
       }  
       // rounding not necessary  
       if (Math.abs(tmp_value) >= 1e15) {  
        return value;  
       }  
      }  
      
      tmp_value = roundHelper(tmp_value, mode);  
      
      if (Math.abs(places) < 23) {  
       if (places > 0) {  
        tmp_value = tmp_value / f1;  
       } else {  
        tmp_value = tmp_value * f1;  
       }  
      } else {  
       var str = "" + tmp_value + "e" + (-places);  
       tmp_value = parseFloat(str);  
       if (!isFinite(tmp_value)) {  
        tmp_value = value;  
       }  
      }  
      
      return tmp_value;  
     };  
    })();
    

    Damit solltest Du wie Du es erwartest runden können, d.h. Math.roundToPlaces(1.255, 2) gibt wirklich 1.26 und nicht 1.25 etc.

    Viele Grüße,
    Christian

    1. Hallo Christian,

      Du kannst aber z.B. auch Banker's Rounding verwenden, dann nutze Math.round()

      Math.round() macht kein Banker's Rounding (hab extra in die Spec geguckt), sondern ist im Wesentlichen equivalent zu floor(x + 0.5). Also wohl was man "round half to positive infinity" nennen könnte.

      Ansonsten halte ich das Gefummel mit dem Runden sowieso etwas für müßig. Wenn man Gleitpunktarithmetik verwendet, hat man an diversen Stellen solche Fehler durch die Zahlendarstellung. Wenn man exakte Ergebnisse braucht, muss man eben ganzzahlige Arithmetik nehmen.

      Grüße

      Daniel

    2. Hi Christian,

      ist ja interessant, dass Du Dich so intensiv mit dem Thema befasst hast...

      Ich habe den Algorithmus mal nach Javascript portiert (ist nicht besonders effizient, funktioniert aber):

      Könntest Du diesen Code auch mal in PHP posten? Den würde ich dann nämlich gern in meinen neuen Wohngeldrechner übernehmen. Zwar erfüllt mein PHP-Script leider nicht die gesetzliche Forderung, dass Dezimalzahlen als Festkommazahlen mit zehn Nachkommastellen zu berücksichtigen sind, aber die Rundung sollte wenigstens stimmen.
      Bislang runde ich z.B. das Einkommen so:
      $Y = floor(($Monatseinkommen+9.99)/10)*10 -5;
      um die Vorgabe "Um „Y“ zu erhalten, ist „Y“ auf den nächsten durch 10 ohne Rest teilbaren vollen Euro-Betrag aufzurunden [... und hiervon] 5 Euro abzuziehen" zu erfüllen.

      freundliche Grüße
      Ingo

      1. Hallo Ingo,

        Könntest Du diesen Code auch mal in PHP posten?

        Werde ich nachher tun.

        Den würde ich dann nämlich gern in meinen neuen Wohngeldrechner übernehmen. Zwar erfüllt mein PHP-Script leider nicht die gesetzliche Forderung, dass Dezimalzahlen als Festkommazahlen mit zehn Nachkommastellen zu berücksichtigen sind, aber die Rundung sollte wenigstens stimmen.
        Bislang runde ich z.B. das Einkommen so:
        $Y = floor(($Monatseinkommen+9.99)/10)*10 -5;
        um die Vorgabe "Um „Y“ zu erhalten, ist „Y“ auf den nächsten durch 10 ohne Rest teilbaren vollen Euro-Betrag aufzurunden [... und hiervon] 5 Euro abzuziehen" zu erfüllen.

        Naja, für diese Logik bräuchtest grundsätzlich einfach nur ein ceil($Monatseinkommen / 10) * 10 - 5; - das klappt auch in bisherigen PHP-Versionen gut [*]. Außerdem würde Dir mein round() nichts nützen, weil das ja zur nächsten Zahl rundet und nicht explizit auf/abrundet - dazu braucht man weiterhin floor() / ceil(). Die zwei Probleme, weswegen mein round() die ganze Rechnung komplizierter macht, decken folgende Fälle ab:

        * Auf sehr hohe/niedrige Nachkommastellenzahl runden (< -22 / > +22), vgl.
           z.B. round(1e24, -24) == 1e24 oder round(1e-24, 24) == 1e-24)
           Ist aber bei Dir nicht der Fall, Du rundest ja auf -1 Stellen
           Genauigkeit.
         * Bei Rundung auf Genauigkeit *hinter* dem Komma (d.h. auf mind. 1
           Nachkommastelle). Du willst aber auf Genauigket *vor* dem Komma runden,
           d.h. da tritt das Problem nicht auf.

        Wenn Du Festkommazahlen willst, kannst Du übrigens auch die bcmath-Erweiterung verwenden...

        Viele Grüße,
        Christian

        [*] Bis auf die FPU-Präzisions-Geschichte, die man in JS aber nie vermeiden kann (da ist man den jew. Browserimplementierungen ausgeliefert) und in reinem PHP auch nicht.

        1. Hi,

          Könntest Du diesen Code auch mal in PHP posten?

          Werde ich nachher tun.

          danke schön - gespeichert für später.

          Naja, für diese Logik bräuchtest grundsätzlich einfach nur ein ceil($Monatseinkommen / 10) * 10 - 5; - das klappt auch in bisherigen PHP-Versionen gut [*]. Außerdem würde Dir mein round() nichts nützen, weil das ja zur nächsten Zahl rundet und nicht explizit auf/abrundet - dazu braucht man weiterhin floor() / ceil().

          stimmt auch wieder.

          * Bei Rundung auf Genauigkeit *hinter* dem Komma (d.h. auf mind. 1
             Nachkommastelle). Du willst aber auf Genauigket *vor* dem Komma runden,
             d.h. da tritt das Problem nicht auf.

          Am Ende der Berechnung habe ich dieses Problem aber doch noch, da das Wohngeld nach der vom Gesetzgeber geforderten kaufmännischen Rundung ("ist auf den nächsten vollen Euro-Betrag aufzurunden, wenn der sich ohne Rundung ergebende restliche Cent-Betrag größer oder gleich 50 ist [...], abzurunden, wenn [...] kleiner als 50 ist.") im Sinne von round-half-up zu runden ist. Und wenn das Ergebnis als floatval ungünstig dargestellt wird, kommt es hier zum besagten Fehler.

          Wenn Du Festkommazahlen willst, kannst Du übrigens auch die bcmath-Erweiterung verwenden...

          Danke für den Tipp; das habe ich jetzt gemacht und auch die Rundung im letzten Schritt läuft damit wohl korrekt. Testweise ergibt round(floatval(bcmul('1.255','100'))) jedenfalls 126 und nicht wie bei round(1.255*100) 125.

          Übrigens ist bcmath bzw. PHP in einer Beziehung ziemlich blöd:
          Ich hatte die Tabellenwerte wie im Gesetz angegeben im Format z.B. 9.102E-5 eingetragen. Diese Notation über strval(9.102E-5) zu "9.102E-005" konvertiert, betrachtet bcmath allerdings als 0. Also musste ich die ganzen Werte erst in das Format z.B. '0.00009102' ändern. :-(

          freundliche Grüße
          Ingo

    3. Hallo nochmal,

      Ich habe einen kleinen Fehler in meiner JS-Version entdeckt: tmp_value wird in der eigentliche Rundungsfunktion nicht als Variable deklariert, hier nochmal die korrigierte Fassung:

      (function () {  
       Math.ROUND_MODE_HALF_UP = 1;  
       Math.ROUND_MODE_HALF_DOWN = 2;  
       Math.ROUND_MODE_HALF_EVEN = 3;  
       Math.ROUND_MODE_HALF_ODD = 4;  
        
       var roundHelper = function (value, mode) {  
        var tmp_value;  
        if (value >= 0.0) {  
         tmp_value = Math.floor(value + 0.5);  
         if ((mode == Math.ROUND_MODE_HALF_DOWN && value == (-0.5 + tmp_value)) ||  
          (mode == Math.ROUND_MODE_HALF_EVEN && value == (0.5 + 2 * Math.floor(tmp_value/2.0))) ||  
          (mode == Math.ROUND_MODE_HALF_ODD && value == (0.5 + 2 * Math.floor(tmp_value/2.0) - 1.0)))  
         {  
          tmp_value -= 1.0;  
         }  
        } else {  
         tmp_value = Math.ceil(value - 0.5);  
         if ((mode == Math.ROUND_MODE_HALF_DOWN && value == (0.5 + tmp_value)) ||  
          (mode == Math.ROUND_MODE_HALF_EVEN && value == (-0.5 + 2 * Math.ceil(tmp_value/2.0))) ||  
          (mode == Math.ROUND_MODE_HALF_ODD && value == (-0.5 + 2 * Math.ceil(tmp_value/2.0) + 1.0)))  
         {  
          tmp_value += 1.0;  
         }  
        }  
        return tmp_value;  
       };  
        
       Math.roundToPlaces = function (value, places, mode) {  
        if (typeof(places) == 'undefined') places = 0;  
        if (typeof(mode) == 'undefined') mode = Math.ROUND_MODE_HALF_UP;  
        
        var f1, f2, tmp_value;  
        var precision_places = 14 - Math.floor(Math.log(Math.abs(value))/Math.LN10);  
        
        f1 = Math.pow(10.0, Math.abs(places));  
        
        if (precision_places > places && precision_places - places < 15) {  
         f2 = Math.pow(10.0, Math.abs(precision_places));  
         if (precision_places >= 0) {  
          tmp_value = value * f2;  
         } else {  
          tmp_value = value / f2;  
         }  
         tmp_value = roundHelper(tmp_value, mode);  
         f2 = Math.pow(10.0, Math.abs(places - precision_places));  
         // places < precision_places  
         tmp_value = tmp_value / f2;  
        } else {  
         if (places >= 0) {  
          tmp_value = value * f1;  
         } else {  
          tmp_value = value / f1;  
         }  
         // rounding not necessary  
         if (Math.abs(tmp_value) >= 1e15) {  
          return value;  
         }  
        }  
        
        tmp_value = roundHelper(tmp_value, mode);  
        
        if (Math.abs(places) < 23) {  
         if (places > 0) {  
          tmp_value = tmp_value / f1;  
         } else {  
          tmp_value = tmp_value * f1;  
         }  
        } else {  
         var str = "" + tmp_value + "e" + (-places);  
         tmp_value = parseFloat(str);  
         if (!isFinite(tmp_value)) {  
          tmp_value = value;  
         }  
        }  
        
        return tmp_value;  
       };  
      })();
      

      Ich habe auf Ingos Wunsch das ganze auch nach PHP portiert:

      define ('ROUND_MODE_HALF_UP', 1);  
      define ('ROUND_MODE_HALF_DOWN', 2);  
      define ('ROUND_MODE_HALF_EVEN', 3);  
      define ('ROUND_MODE_HALF_ODD', 4);  
        
      function _round_helper($value, $mode) {  
       if ($value >= 0.0) {  
        $tmp_value = floor($value + 0.5);  
        if (($mode == ROUND_MODE_HALF_DOWN && $value == (-0.5 + $tmp_value)) ||  
         ($mode == ROUND_MODE_HALF_EVEN && $value == (0.5 + 2 * floor($tmp_value / 2.0))) ||  
         ($mode == ROUND_MODE_HALF_ODD && $value == (0.5 + 2 * floor($tmp_value / 2.0) - 1.0)))  
        {  
         $tmp_value -= 1.0;  
        }  
       } else {  
        $tmp_value = ceil($value - 0.5);  
        if (($mode == ROUND_MODE_HALF_DOWN && $value == (0.5 + $tmp_value)) ||  
         ($mode == ROUND_MODE_HALF_EVEN && $value == (-0.5 + 2 * ceil($tmp_value / 2.0))) ||  
         ($mode == ROUND_MODE_HALF_ODD && $value == (-0.5 + 2 * ceil($tmp_value / 2.0) + 1.0)))  
        {  
         $tmp_value += 1.0;  
        }  
       }  
       return $tmp_value;  
      }  
        
      /**  
       * Round to a certain precision  
       *  
       * This method rounds to the nearest number of a certain precision in the  
       * specified rounding mode. Available modes are:  
       *  
       * ROUND_MODE_HALF_UP: Round .5 away from zero (arithmetic rounding)  
       * ROUND_MODE_HALF_DOWN: Round .5 away towards zero  
       * ROUND_MODE_HALF_EVEN: Round .5 to nearest even number (banker's rounding)  
       * ROUND_MODE_HALF_ODD: Round .5 to nearest odd number  
       *  
       * Please note that PHP does not have a platform-independent FP behaviour  
       * as of PHP 5.2. This will likely change in PHP 5.3. Until then, this method  
       * may still exhibit some strange results in very rare circumstances.  
       *  
       * This is a direct port of the C algorithm in  
       * <http://wiki.php.net/rfc/rounding>. Please note that this code is likely  
       * to be obsolete in PHP 5.3 as PHP's own round() function will likely share the  
       * same signature and implementation as this function.  
       *  
       * @param double $value The value to round  
       * @param int $places The number of places the result should have, may be  
       *                    negative for rounding to 10s or 100s etc.  
       * @param int $mode The rounding mode.  
       * @return double The rounded value  
       */  
      function round_to_places($value, $places = 0, $mode = ROUND_MODE_HALF_UP) {  
       $places = (int)$places;  
       $value = (double)$value;  
        
       $precision_places = 14 - (int)floor(log10(abs($value)));  
       $f1 = pow(10.0, abs($places));  
        
       if ($precision_places > $places && $precision_places - $places < 15) {  
        $f2 = pow(10.0, abs($precision_places));  
        if ($precision_places >= 0) {  
         $tmp_value = $value * $f2;  
        } else {  
         $tmp_value = $value / $f2;  
        }  
        $tmp_value = _round_helper($tmp_value, $mode);  
        $f2 = pow(10.0, $precision_places - $places);  
        $tmp_value /= $f2;  
       } else {  
        if ($places >= 0) {  
         $tmp_value = $value * $f1;  
        } else {  
         $tmp_value = $value / $f1;  
        }  
        // rounding not necessary  
        if (abs($tmp_value) >= 1e15) {  
         return $value;  
        }  
       }  
        
       $tmp_value = _round_helper($tmp_value, $mode);  
        
       if (abs($places) < 23) {  
        if ($places > 0) {  
         $tmp_value = $tmp_value / $f1;  
        } else {  
         $tmp_value = $tmp_value * $f1;  
        }  
       } else {  
        $str = sprintf("%15fe%d", $tmp_value, -$places);  
        $tmp_value = (double)$str;  
        if (!is_finite($tmp_value) || is_nan($tmp_value)) {  
         return $value;  
        }  
       }  
        
       return $tmp_value;  
      }
      

      Viele Grüße,
      Christian