Zum Beispiel möchte ich eine Liste von Schaltflächen von 0,0.5, ... 5 anzeigen, die für jede 0,5 springt. Ich benutze eine for-Schleife, um das zu tun, und habe eine andere Farbe auf der Schaltfläche STANDARD_LINE:
var MAX=5.0;
var DIFF=0.5
var STANDARD_LINE=1.5;
for(var i=0;i<=MAX;i=i+DIFF){
button.text=i+'';
if(i==STANDARD_LINE){
button.color='red';
}
}
In diesem Fall sollte es keine Rundungsfehler geben, da jeder Wert in IEEE 754 genau ist. Ich habe jedoch Probleme, ihn zu ändern, um einen Gleitkomma-Gleichheitsvergleich zu vermeiden:
var MAX=10;
var STANDARD_LINE=3;
for(var i=0;i<=MAX;i++){
button.text=i/2.0+'';
if(i==STANDARD_LINE/2.0){
button.color='red';
}
}
Einerseits ist der ursprüngliche Code einfacher und geht an mich weiter. Aber eines überlege ich mir: Führt i == STANDARD_LINE Junior-Teamkollegen in die Irre? Verbirgt sich die Tatsache, dass Gleitkommazahlen Rundungsfehler aufweisen können? Nach dem Lesen von Kommentaren aus diesem Beitrag:
Es scheint, dass es viele Entwickler gibt, die nicht wissen, dass einige Gleitkommazahlen genau sind. Sollte ich Gleichheitsvergleiche mit Gleitkommazahlen vermeiden, auch wenn sie in meinem Fall gültig sind? Oder bin ich überlegt?
i
wird aber immer nur eine ganze Zahl in der zweiten Auflistung sein. Versuchen Sie, die zweite zu entfernen/2.0
.button
sich an keiner Stelle in Ihrer Schleife etwas ändert. Wie wird auf die Liste der Schaltflächen zugegriffen? Über einen Index in ein Array oder einen anderen Mechanismus? Wenn es sich um einen Indexzugriff auf ein Array handelt, ist dies ein weiteres Argument für die Umstellung auf Ganzzahlen.Antworten:
Aufeinanderfolgende Gleitkommaoperationen würde ich immer vermeiden, es sei denn, das Modell, das ich berechne, erfordert sie. Gleitkomma-Arithmetik ist für die meisten nicht intuitiv und eine wichtige Fehlerquelle. Und die Fälle, in denen es Fehler verursacht, von denen zu unterscheiden, in denen dies nicht der Fall ist, ist eine noch subtilere Unterscheidung!
Daher ist die Verwendung von Floats als Schleifenzähler ein Fehler, der darauf wartet, dass er auftritt, und der mindestens einen dicken Hintergrundkommentar erfordert, der erklärt, warum es in Ordnung ist, hier 0,5 zu verwenden, und dass dies vom spezifischen numerischen Wert abhängt. Zu diesem Zeitpunkt ist es wahrscheinlich besser lesbar, den Code neu zu schreiben, um Fließkommazähler zu vermeiden. Und Lesbarkeit ist neben Korrektheit in der Hierarchie der beruflichen Anforderungen.
quelle
DIFF must be an exactly-representable double that evenly divides STANDARD_LINE
. Wenn Sie diesen Kommentar nicht schreiben möchten (und sich darauf verlassen möchten, dass alle zukünftigen Entwickler genug über IEEE754 binary64-Gleitkomma wissen, um ihn zu verstehen), schreiben Sie den Code nicht auf diese Weise. dh schreibe den Code nicht so. Vor allem, weil es wahrscheinlich nicht effizienter ist: FP-Addition hat eine höhere Latenz als Integer-Addition und ist eine schleifenabhängige Abhängigkeit. Compiler (auch JIT-Compiler?) Sind wahrscheinlich besser darin, Schleifen mit Ganzzahlzählern zu erstellen.In der Regel sollten Schleifen so geschrieben werden, dass Sie überlegen, etwas n- mal zu tun . Wenn Sie Gleitkommaindizes verwenden, müssen Sie nicht mehr n- mal etwas tun, sondern müssen so lange ausgeführt werden, bis eine Bedingung erfüllt ist. Wenn dieser Zustand dem sehr ähnlich ist, den
i<n
so viele Programmierer erwarten, dann scheint der Code eine Sache zu tun, wenn er tatsächlich eine andere tut, was von Programmierern, die den Code überfliegen, leicht falsch interpretiert werden kann.Es ist etwas subjektiv, aber meiner bescheidenen Meinung nach sollten Sie dies tun, wenn Sie eine Schleife neu schreiben können, um einen Ganzzahlindex zu verwenden, um eine feste Anzahl von Schleifen auszuführen. Betrachten Sie also die folgende Alternative:
Die Schleife funktioniert in ganzen Zahlen. In diesem Fall
i
handelt es sich um eine Ganzzahl, die ebenfallsSTANDARD_LINE
in eine Ganzzahl umgewandelt wird. Dies würde natürlich die Position Ihrer Standardlinie ändern, falls es zu einer Abrundung kommenMAX
sollte. Sie sollten also weiterhin versuchen, eine Abrundung zu verhindern, um ein genaues Rendern zu erzielen. Sie haben jedoch immer noch den Vorteil, dass Sie die Parameter in Pixel und nicht in ganzen Zahlen ändern können, ohne sich um den Vergleich von Gleitkommawerten kümmern zu müssen.quelle
i
undSTANDARD_LINE
nur wie ganze Zahlen aussehen. Es gibt keinen Zwang überhaupt, undDIFF
,MAX
undSTANDARD_LINE
sind alle nurNumber
s.Number
s, die als Ganzzahlen verwendet werden, sollten unterhalb sicher sein2**53
, sie sind jedoch immer noch Gleitkommazahlen.Ich stimme allen anderen Antworten zu, dass die Verwendung einer nicht ganzzahligen Schleifenvariablen im Allgemeinen ein schlechter Stil ist, selbst in Fällen wie diesem, in denen sie korrekt funktioniert. Aber es scheint mir, dass es einen anderen Grund gibt, warum es hier einen schlechten Stil gibt.
Ihr Code "weiß", dass die verfügbaren Linienbreiten genau ein Vielfaches von 0,5 von 0 bis 5,0 sind. Sollte es? Es scheint, dass dies eine Benutzeroberflächenentscheidung ist, die sich leicht ändern kann (z. B. möchten Sie, dass die Lücken zwischen den verfügbaren Breiten größer werden als die Breiten. 0,25, 0,5, 0,75, 1,0, 1,5, 2,0, 2,5, 3,0, 4,0, 5.0 oder so).
Ihr Code "weiß", dass alle verfügbaren Linienbreiten "schöne" Darstellungen sowohl als Gleitkommazahlen als auch als Dezimalzahlen haben. Das scheint sich auch zu ändern. (Möglicherweise möchten Sie irgendwann 0,1, 0,2, 0,3, ...)
Ihr Code "weiß", dass der Text auf den Schaltflächen einfach das ist, was Javascript aus diesen Gleitkommawerten macht. Das scheint sich auch zu ändern. (Beispiel: Vielleicht möchten Sie eines Tages Breiten wie 1/3, die Sie wahrscheinlich nicht als 0,33333333333333 oder was auch immer anzeigen möchten. Oder Sie möchten aus Konsistenzgründen "1,0" anstelle von "1" mit "1,5" anzeigen. .)
All dies fühlt sich für mich wie Manifestationen einer einzigen Schwäche an, die eine Art Vermischung von Schichten darstellt. Diese Gleitkommazahlen sind Teil der internen Logik der Software. Der auf den Schaltflächen angezeigte Text ist Teil der Benutzeroberfläche. Sie sollten getrennter sein als im Code hier. Begriffe wie "Welcher von diesen ist der Standard, der hervorgehoben werden sollte?" sind Fragen der Benutzeroberfläche, und sie sollten wahrscheinlich nicht an diese Gleitkommawerte gebunden werden. Und Ihre Schleife hier ist wirklich (oder sollte es zumindest sein) eine Schleife über Schaltflächen , nicht über Linienbreiten . So geschrieben, verschwindet die Versuchung, eine Schleifenvariable mit nicht ganzzahligen Werten zu verwenden: Sie würden nur aufeinanderfolgende ganze Zahlen oder eine for ... in / for ... of-Schleife verwenden.
Ich bin der Meinung, dass die meisten Fälle, in denen man versucht sein könnte, eine Schleife über nicht ganzzahlige Zahlen zu erstellen, wie folgt aussehen: Es gibt andere Gründe, die sich nicht auf numerische Probleme beziehen, weshalb der Code anders organisiert werden sollte. (Nicht in allen Fällen; ich kann mir vorstellen, dass einige mathematische Algorithmen am besten in Form einer Schleife über nicht ganzzahlige Werte ausgedrückt werden.)
quelle
Ein Code-Geruch ist die Verwendung von Floats in einer solchen Schleife.
Looping kann auf viele Arten durchgeführt werden, aber in 99,9% der Fälle sollten Sie sich an ein Inkrement von 1 halten, da es sonst definitiv zu Verwirrung kommt, nicht nur bei jungen Entwicklern.
quelle
Ja, das möchten Sie vermeiden.
Gleitkommazahlen sind eine der größten Fallen für den ahnungslosen Programmierer (was meiner Erfahrung nach fast jeden betrifft). Von der Abhängigkeit von Fließkomma-Gleichheitstests bis zur Darstellung von Geld als Fließkomma ist alles ein großer Sumpf. Einer der größten Straftäter ist das Aufsummieren eines Schwimmers auf den anderen. Es gibt ganze Bände wissenschaftlicher Literatur über solche Dinge.
Verwenden Sie Gleitkommazahlen genau an den Stellen, an denen sie angebracht sind, zum Beispiel, wenn Sie mathematische Berechnungen dort durchführen, wo Sie sie benötigen (wie Trigonometrie, Zeichnen von Funktionsgraphen usw.), und gehen Sie bei seriellen Operationen äußerst vorsichtig vor. Gleichstellung ist richtig. Das Wissen darüber, welche bestimmte Menge von Zahlen nach IEEE-Standards genau ist, ist sehr geheimnisvoll und ich würde mich niemals darauf verlassen.
In Ihrem Fall, es wird durch Murphy Law, kommt den Punkt , wo Management will Sie nicht 0,0 haben, 0,5, 1,0 ... aber 0,0, 0,4, 0,8 ... oder was auch immer; Sie werden sofort verdrängt und Ihr Junior-Programmierer (oder Sie selbst) wird lange und hart debuggen, bis Sie das Problem finden.
In Ihrem speziellen Code hätte ich in der Tat eine Integer-Loop-Variable. Es stellt die
i
th-Taste dar, nicht die laufende Nummer.Und ich würde wahrscheinlich der zusätzlichen Klarheit halber nicht schreiben,
i/2
aberi*0.5
das macht reichlich klar, was los ist.Hinweis: Wie in den Kommentaren erwähnt, hat JavaScript keinen eigenen Typ für Ganzzahlen. Ganzzahlen mit bis zu 15 Stellen sind jedoch garantiert genau / sicher (siehe https://www.ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer ), daher für Argumente wie dieses ("ist es") verwirrender / fehleranfälliger für die Arbeit mit Ganzzahlen oder Nicht-Ganzzahlen Im täglichen Gebrauch (Schleifen, Bildschirmkoordinaten, Array-Indizes usw.) gibt es keine Überraschungen, wenn Ganzzahlen
Number
als JavaScript dargestellt werden.quelle
Ich denke nicht, dass einer Ihrer Vorschläge gut ist. Stattdessen würde ich eine Variable für die Anzahl der Schaltflächen basierend auf dem Maximalwert und dem Abstand einführen. Dann ist es einfach genug, die Indizes der Schaltfläche selbst zu durchlaufen.
Es könnte mehr Code sein, aber es ist auch lesbarer und robuster.
quelle
Sie können das Ganze vermeiden, indem Sie den angezeigten Wert berechnen, anstatt den Schleifenzähler als Wert zu verwenden:
quelle
Fließkomma-Arithmetik ist langsam und Ganzzahl-Arithmetik ist schnell. Wenn ich also Fließkomma verwende, würde ich es nicht unnötig verwenden, wenn Ganzzahlen verwendet werden können. Es ist nützlich, sich Fließkommazahlen und sogar Konstanten immer als ungefähr mit einem kleinen Fehler vorzustellen. Es ist beim Debuggen sehr nützlich, native Gleitkommazahlen durch Plus- / Minus-Gleitkommaobjekte zu ersetzen, bei denen Sie jede Zahl als Bereich anstelle eines Punkts behandeln. Auf diese Weise entdecken Sie nach jeder arithmetischen Operation zunehmende Ungenauigkeiten. "1.5" sollte also als "eine Zahl zwischen 1,45 und 1,55" und "1.50" als "eine Zahl zwischen 1,495 und 1,505" angesehen werden.
quelle