Frank: genau Rechnen in JS

Halli Hallo,
sagt mal, kann man in JS nicht mit Kommastellen rechnen?
Wenn ich eine Def var x = 1.6 mache und es dann im Programm mal testhalber mit alert ausgebe, dann kommt sowas wie 1.0000000000001 raus... Ich bräuchte aber den exakten Wert, da er eine Abbruchbedingung für ein Schleife sein soll:
if(y == 1.6) return;
Das wird ja dann leider nichts.
Dann dachte ich ans runden, aber mit round kann ich soweit ich gelesen habe nur ganzzahlig runden.

Gibt es da andere Möglichkeiten exakt zu rechnen oder hat das JS so an sich? (bin da noch nicht so lange dran)

Vielsen Dank für eure Hilfe

Frank

  1. Kleiner Nachtrag: die ungenaue Zahl tritt nach einer Addition auf:

    var x=1.6;
    x=x+0.1;
    alert(x);

    gibt 1.70000000000000002

    Wieso um Himmels willen...das passt mir gar nicht!

    PS: und "return" meinte ich natürlich als Abbruch einer Funktion.

    Viele Grüße

    1. Hallo,

      hast du schon mal die ArchivSuche ausprobiert?

      gruss

      --
      no strict;
      no warnings;
      Der natürliche Feind der Festplatte ist der Teppich, der sich gerne mal elektrisch aufläd und der Festplatte eine wischt.
      Kluge Leute sind auch nur Menschen.
      1. Ehrlich gesagt, ja.
        "rechnen Javascript" gibt aber nicht viel her...
        Über einen Tipp würde ich mich freun :)

        1. Ehrlich gesagt, ja.
          "rechnen Javascript" gibt aber nicht viel her...
          Über einen Tipp würde ich mich freun :)

          "Friendship means saying "What? You too? I thought I am the only one!"

          http://forum.de.selfhtml.org/archiv/2005/4/t106621/#m660975

          Willkommen im Club *g*

          Schönen abend noch,
          Jörg

          1. Ok, habe mit eurer Hilfe diese schöne Fkt. gefunden, die mich jetzt gerettet hat:

            function dezRound(num,pos) {
              if(pos<0) {
                pos=Math.pow(10,Math.abs(pos));
                return Math.round(num/pos)*pos;
              }
              else if(pos>0) {
                pos=Math.pow(10,pos);
                return Math.round(num*pos)/pos;
              }
              else {
                return Math.round(num);
              }
            }

            Danke

            1. Hi,

              Ok, habe mit eurer Hilfe diese schöne Fkt. gefunden, die mich jetzt gerettet hat:

              function dezRound(num,pos)

              Ja, eine nette Rundungsfunktion. Warum aber möchtest Du nicht das Problem selber angehen und bevorzugst einen mühseligen "Würg-around"?
              Was ist mit toFixed() und  toPrecision() aus Selfhtml?
              Erst in Javascript 1.5? Gut, kann ein Argument sein, gebe ich zu.

              if(y == 1.6) return;
              Das wird ja dann leider nichts.

              Nein, das wird nix. Das liegt daran, das Du im Dezimalsystem notierst und der Computer aber nur Binär versteht und umrechnen muß. zweimal in Deinem Fall. Das kann schonmal zu Verlusten führen.
              Dieser Fehler ist jedoch genau definierbar und heißt meist Epsilon "ε". Normalerweise wird der auch irgendwo als Wert zur Verfügung gestellt, ist in Javascript jedoch nicht zu finden. Warum auch immer. Da ε architekturabhängig ist läßt er sich also nur rechnerisch ermitteln. Eine, wenn auch recht brutale Methode ist:

              var tmp1    = 1.0;
              var tmp2    = 0.0;
              var EPSILON = 0.0;
              do{
                mchEps = tmp1;
                tmp1  /= 2;
                tmp2   = 1.0 + tmp1;
              }while (tmp2 > 1.0);

              "tmp1" wird so lange halbiert (ist ja ein Binärsystem!) bis es so klein ist, das 1.0 plus des Wertes aus tmp1 nicht mehr größer als 1.0 ist. Das heißt übrigens nicht, das beide exakt gleich sind! Die Rechnung oben geht übrigens auch in Javascript recht flott und kann bei Bedarf entsprechend eingesetzt werden, onload böte sich da an.

              Mit dem so gewonnenem Wert kann nun verglichen werden.

              Statt

              if(wert1 == wert2)

              wäre dieses passender

              if(Math.abs(wert1 - wert2) < Math.abs(wert1 * EPSILON)){
                alert(wert1 + " und " + wert2 + " sind ziemlich gleich");
              }
              else{
                alert(wert1 + " und " + wert2 + " sind nicht gleich");
              }

              Aus Deinem

              if(y == 1.6) return;

              würde also

              if(Math.abs(x - 1.6) < Math.abs(x * EPSILON)) return;

              und ausreichend funktionieren.

              Ja, das _ist_ kompliziert, zumindest auf den ersten Blick.
              Aber diese Schwierigkeiten bestehen seitdem das Binärsystem auf Computern angewandt wird und ist somit ein sehr gut bekanntes Feld.

              Da das aber auch viel Schreiberei ist und daher fehleranfällig, würde ich vorschlagen, da eine eigene Funktion draus zu machen.

              so short

              Christoph Zurnieden

              1. Danke für Deine ausführliche Antwort ! Freut mich sehr.
                Wieso habe andere Sprachen dann nicht dieses Problem? Also zumindest so offensichtlich.

                1. Hallo Frank,

                  Wieso habe andere Sprachen dann nicht dieses Problem? Also zumindest so offensichtlich.

                  "Andere" Sprachen verwenden bei der Ausgabe Formatanweisungen, die ein Runden zur Folge haben, bzw. es wird automatisch auf die letzte zuverlässige Stelle gerundet.

                  Zum Problem mit dem Vergleich: ich habe in den Programmierkursen, die ich besucht habe, immer gelernt, das die Abfrage auf Gleichheit bei Fließkommazahlen nicht immer den gewünschten Effekt hat und daher gemieden werden sollte. Dein Problem ist also nicht Javascripttypisch. Und bei den meisten Problemen recht es zu wissen, dass man das Ziel überschritten hat, und nicht, dass man auf der Ziellinie steht.

                  Gruß, Jürgen

                2. Hi,

                  Wieso habe andere Sprachen dann nicht dieses Problem? Also zumindest so offensichtlich.

                  Alle Sprachen haben das, auch wenn manche das gut verstecken können oder intern andere Methoden verwenden, es ist ein prinzipielles Problem. Der Computer kann nur bis zwei zählen (Binärsystem), Du kannst jedoch bis 10 zählen (Dezimalsystem)und tust es auch. In den meisten Programmiersprachen kannst Du Zahlen auf verschiedene Weisen eingeben. Meistens bevorzugst Du dabei natürlich das Dezimalsystem, das hast Du schließlich schon im Kindergarten gelernt. Im Gegensatz zu Dir versteht die Mehrheit der Rechner aber _nur_ das Binärsystem und muß daher umrechnen. So und nun rechne mal dezimal 0,1 in das Binärsystem um, das ergibt: 0.001100110011100110011 ...
                  Das zweite Problem ist der strikt begrenzte Speicherplatz. Beides zusammen ergibt dann Dein Problem mit der Genauigkeit. Aber das ist in Wikipedia zusammen mit http://www.kmkorn.de/artikel/fp/fp.htm recht gut erklärt finde ich.
                  Es gibt im Netz bestimmt noch viel mehr darüber, aber obige Seiten liefern genügend Stichworte für eine Googlesuche.
                  Wenn Du Englisch kannst z.B. diese Seite:http://docs.hp.com/en/B3906-90006/ch02s02.html bzw die hier http://ae-www.technion.ac.il/InfoPages/FAQ/programming/floatrep.memo

                  Wie sich das Problem in einer anderen Programmiersprache darstellt steht z.B. ... ah ... hier:http://www.scism.sbu.ac.uk/law/Section2/chap1/s2c1p2b5.html

                  Deine Rundungsfehler sind also in Wirklichkeit keine Fehler sondern durchaus mit voller Absicht so gesetzt und dann mit den üblichen drei Durchschlägen abgestempelt und genormt (IEEE 754).
                  "It's not a bug, it's a feature!" ;-)

                  so short

                  Christoph Zurnieden