Rechenfehler in JavaScript
Andreas Lindig
- javascript
0 Struppi0 JürgenB0 Cybaer0 Andreas Lindig0 JürgenB
Hallo Forum,
ích bekomme in JavaScript folgenden Rechenfehler:
2937.7 - 326.3 = 2611.3999999999996
richtig wäre 2611.4
Nun könnte man sagen: "mein jott, watt is der Kerl aber auch empfindlich..." Das Problem ist nur, daß ich in einer Schleife jeweils den gleichen Betrag vom Ergebnis wieder abziehe und nach soundsovielen Schritten _genau_ auf einer vorbestimmten Zahl rauskommen muß, sonst fehlt ein Schleifendurchgang. Ich habe jetzt die Abbruchbedingung dahingehend geändert, daß der Endwert auch noch um 0.1 unterschritten werden kann und es Funktioniert - bei bisherigen Tests. Aber ich weiß nicht, was da in anderen Konstellationen noch für Abweichungen entstehen; ich finde diese Lösung ein wenig dirty. Kann ich JS nicht anweisen so simple Rechnungen bitteschön genau auszuführen?
Gruß, Andreas
ích bekomme in JavaScript folgenden Rechenfehler:
2937.7 - 326.3 = 2611.3999999999996
richtig wäre 2611.4
Auch hier <seufz>
Du kannst mit Binären Zahlen nie genau Fließkommaberechnungen machen.
http://www3.futureware.at/artikel/zahlen.htm
Struppi.
你好 Struppi,
Du kannst mit Binären Zahlen nie genau Fließkommaberechnungen machen.
Naja, das stimmt so ja nicht. Binaer-Zahlen schieben nur die Problem-Zahlen
auf andere Stellen; dafuer ist dann das, was in dezimal problematisch
waere, in binaer uU recht einfach.
再见,
克里斯蒂安
Du kannst mit Binären Zahlen nie genau Fließkommaberechnungen machen.
Naja, das stimmt so ja nicht. Binaer-Zahlen schieben nur die Problem-Zahlen
auf andere Stellen; dafuer ist dann das, was in dezimal problematisch
waere, in binaer uU recht einfach.
so verstehe ich das jetzt auch, also Problemzahlen bei diesen Berechnungen sind in Binärschreibweise Endlosbrüche oder so was ja?
@Struppi: danke für den Link. Da wird von "E-Bereich" gesprochen, nur wie finde ich denn den relevanten Bereich? Ich weiß doch vorher gar nicht, zu wieviel sich die Fehler in der Schleife addiert haben.
Gruß, Andreas
你好 Andreas,
so verstehe ich das jetzt auch, also Problemzahlen bei diesen
Berechnungen sind in Binärschreibweise Endlosbrüche oder so was ja?
Jain, sie koennen auch periodisch sein. Bestes Beispiel: rechne mal
0,1 dezimal um nach binaer :)
再见,
克里斯蒂安
so verstehe ich das jetzt auch, also Problemzahlen bei diesen
Berechnungen sind in Binärschreibweise Endlosbrüche oder so was ja?Jain, sie koennen auch periodisch sein.
hat für mich dazugezählt :-) mein mathematisches Differenzierungsvermögen sollte doch hier bekannt sein ;-)
Gruß, Andreas
Hallo Andreas Lindig,
ích bekomme in JavaScript folgenden Rechenfehler:
2937.7 - 326.3 = 2611.3999999999996
richtig wäre 2611.4
ich glaube, da erwartest Du etwas zu viel. Die Abweichung befindet sich in der 17. Stelle. Das ist besser als "double precision". "Normale" Rechner mit "normalen" Programmiersprachen können nicht mehr. Runde doch einfach mit z.B. toFixed oder mit mal 10^n, Math.round, durch 10^n.
Gruß, Jürgen
Hi,
Ich habe jetzt die Abbruchbedingung dahingehend geändert, daß der Endwert auch noch um 0.1 unterschritten werden kann und es Funktioniert - bei bisherigen Tests. Aber ich weiß nicht, was da in anderen Konstellationen noch für Abweichungen entstehen;
Runde doch einfach ein wenig?!
Gruß, Cybaer
zu den Rundungsvorschlägen:
Ich muß eine Strecke - z.B. zwischen 127 und 16384 - in eine bestimmte Anzahl gleichmäßige Abschnitte teilen, z.B. in 17 Stück. Dazu berechne ich die Länge des Abschnitts (16384-127)/(17-1) und in der Schleife gehe ich immer in dieser Schrittweite voran. Wenn ich jedes Zwischenergebnis runden würde, käme ich ja ganz woanders am Ende raus, also runde ich absichtlich nicht. Ich könnte nur runden, wenn ich wüßte, an welcher Stelle die Abweichung auftritt. Im Ausgangsbeispiel 2611.3999999999996 ^= 2611.4 wüße ich: erste Nachkommastelle, aber die Abweichung kann ja immer woanders sein.
Mein Programm wird jetzt mit meiner Pi-mal-Daumen-Toleranz-Zugabe wohl funktionieren, aber interessant ist das Problem ja trotzdem mal ;-)
Gruß, Andreas
Hallo Andreas Lindig,
bei wiederholten Additionen wirst Du immer Probleme mit der Ungenauigkeit haben, da sich die Fehler ja addieren. Evtl. kannst Du das Problem umgehen, wenn Du multiplizierst. Also statt
wert = startwert;
for(i=0;i<ende;i++) {
...
wert = wert + increment;
}
besser
for(i=0;i<ende;i++) {
wert = startwert + i*increment;
...
}
Gruß, Jürgen
你好 JürgenB,
besser
for(i=0;i<ende;i++) {
wert = startwert + i*increment;
...
}
Eh, das ist aber doch dasselbe wie `wert = startwert + ende * increment`{:.language-javascript} -- wozu dann ueberhaupt noch die Schleife?
再见,
克里斯蒂安
--
Ich bewundere wirklich den Sinn der Bienen für kollektive Verantwortung. Obwohl sich einzelne Bienen hin und wieder bekämpfen, herrscht zwischen Ihnen grundsätzlich ein starkes Gefühl für Eintracht und Zusammenarbeit. Wir Menschen gelten als sehr viel weiter entwickelt, doch mitunter rangieren wir sogar hinter kleinen Insekten.
Eh, das ist aber doch dasselbe wie
wert = startwert + ende * increment
-- wozu dann ueberhaupt noch die Schleife?
weil ich alle Zwischenergebnisse brauche. Ich will die Punkte wissen, wo ich bei gleichmäßiger Teilung der Strecke sozusagen die Pfähle einschlagen muß.
Gruß, Andreas
Hallo,
weil ich alle Zwischenergebnisse brauche. Ich will die Punkte wissen, wo ich bei gleichmäßiger Teilung der Strecke sozusagen die Pfähle einschlagen muß.
Das geht nur in begrenzter Genauigkeit. Die Additionsungenauigkeit bekommst Du aber weg, indem Du die Positionen der Pfähle nach der Berechnung so genau, wie erforderlich, rundest.
Beispiel:
var start = 127;
var ende = 16384;
var teile = 10
var schrittw = (ende-start)/teile;
document.writeln("Strecke = " + (ende-start) + "<br>");
document.writeln("Schrittweite = " + schrittw + "<br>");
var strecke = 0;
for (i=0; i<teile; i++) {
strecke = Math.round((strecke + schrittw)*100)/100;
document.writeln("Nach Teil " + (i+1) + " ist Strecke = " + strecke + "<br>");
}
Ohne Rundung käme heraus:
Strecke = 16257
Schrittweite = 1625.7
Nach Teil 1 ist Strecke = 1625.7
Nach Teil 2 ist Strecke = 3251.4
Nach Teil 3 ist Strecke = 4877.1
Nach Teil 4 ist Strecke = 6502.8
Nach Teil 5 ist Strecke = 8128.5
Nach Teil 6 ist Strecke = 9754.2
Nach Teil 7 ist Strecke = 11379.900000000001
Nach Teil 8 ist Strecke = 13005.600000000002
Nach Teil 9 ist Strecke = 14631.300000000003
Nach Teil 10 ist Strecke = 16257.000000000003
Aber 16257 _ist_ nunmal _dezimal_ in 10 gleiche Teile teilbar. Die gerundete Variante kann das auch.
Willst Du aber bspw. nur 7 Teile, dann kommt es halt dauf an, mit welcher Abweichung Du leben kannst. Beispielsweise wäre die, mit Rundung auf 4 Nachkommastellen (Math.round((strecke + schrittw)*10000)/10000;):
Strecke = 16257
Schrittweite = 2322.4285714285715
Nach Teil 1 ist Strecke = 2322.4286
Nach Teil 2 ist Strecke = 4644.8572
Nach Teil 3 ist Strecke = 6967.2858
Nach Teil 4 ist Strecke = 9289.7144
Nach Teil 5 ist Strecke = 11612.143
Nach Teil 6 ist Strecke = 13934.5716
Nach Teil 7 ist Strecke = 16257.0002
Denn 16257 ist nunmal auch dezimal nicht endlich in 7 Teile teilbar.
______
2322,428571
viele Grüße
Axel
Hallo Christian Kruse,
Eh, das ist aber doch dasselbe wie
wert = startwert + ende * increment
-- wozu dann ueberhaupt noch die Schleife?
stimmt, aber nur wenn die Zwischenwerte nicht benötigt werden. Daher die "..." in der Schleife.
Gruß, Jürgen