Stoppt diese ‘for’-Schleife und warum/warum nicht? für (var i=0; 1/i > 0; i++) { }

Lesezeit: 6 Minuten

Benutzer-Avatar
Max Koretskyi

Macht dies for Schleife jemals aufhören?

for (var i=0; 1/i > 0; i++) {
}

Wenn ja, wann und warum? Mir wurde gesagt, dass es aufhört, aber mir wurde kein Grund dafür gegeben.

Aktualisieren

Als Teil der Untersuchung habe ich einen ziemlich langen und detaillierten Artikel geschrieben, der alles erklärt, was unter der Motorhaube vor sich geht – Hier ist, was Sie über den Zahlentyp von JavaScript wissen müssen

  • Es wird nicht aufhören. Versuchen Sie, dieses Stück Code auszuführen. for (var i=0; 1/i > 0; i++) { console.log(i) }

    – Surabh Agrawal

    15. Juni 2016 um 5:54 Uhr

  • Number.MAX_VALUE + 9.979202e291 == „Infinity“ und 1/ (NaN oder „Infinity“ oder „undefined“) > 0 == false.

    – Bitte

    15. Juni 2016 um 6:54 Uhr


  • Wird Javascript diese Schleife ignorieren, weil sie keine Anweisungen enthält? dh Optimieren Sie es weg? Ich weiß, dass es einige kompilierte Sprachen gibt, die dies tun würden.

    – Brian J

    15. Juni 2016 um 12:54 Uhr

  • @askeet, wie gotnull und andere unten betonen, erreichen wir Infinity nie, indem wir wiederholt inkrementieren, sondern verfangen uns danach in einer Schleife Number.MAX_SAFE_INTEGER + 1.

    – LSpice

    15. Juni 2016 um 13:57 Uhr

(Ich bin kein Fan von Meta-Inhalten, aber: Die Antworten von gotnull und le_m sind sowohl richtig als auch nützlich. Sie waren ursprünglich und sind es umso mehr mit den Änderungen, die nach der Veröffentlichung dieses Community-Wikis vorgenommen wurden. Die ursprüngliche Motivation für dieses CW ist aufgrund dieser Änderungen weitgehend verschwunden, aber es bleibt nützlich, also … Außerdem: Während nur ein paar Autoren aufgelistet sind, haben viele andere Community-Mitglieder mit Kommentaren, die gefaltet wurden, sehr geholfen rein und aufgeräumt. Dies ist nicht nur dem Namen nach ein CW.)


Die Schleife wird in einer korrekt implementierten JavaScript-Engine nicht enden. (Die Host-Umgebung der Engine könnte sie schließlich beenden, weil sie endlos ist, aber das ist eine andere Sache.)

Hier ist der Grund:

  1. Zunächst wann i ist 0die Bedingung 1/i > 0 ist wahr, weil in JavaScript 1/0 ist Infinityund Infinity > 0 ist wahr.

  2. Danach, i wird inkrementiert und wächst als positiver ganzzahliger Wert für eine lange Zeit weiter (weitere 9.007.199.254.740.991 Iterationen). In all diesen Fällen 1/i wird bleiben > 0 (obwohl die Werte für 1/i erhalten Ja wirklich klein gegen Ende!) und so geht die Schleife weiter bis einschließlich der Schleife wo i erreicht den Wert Number.MAX_SAFE_INTEGER.

  3. Zahlen in JavaScript sind binäre Fließkommazahlen nach IEEE-754 mit doppelter Genauigkeit, ein ziemlich kompaktes Format (64 Bit), das schnelle Berechnungen und einen großen Bereich ermöglicht. Dies geschieht, indem die Zahl als Vorzeichenbit, 11-Bit-Exponent und 52-Bit-Signifikand gespeichert wird (obwohl durch Cleverness tatsächlich 53 Bit Genauigkeit erreicht werden). Es ist binär (Basis 2) Fließkommazahl: Der Mantifikand (plus etwas Cleverness) gibt uns den Wert und der Exponent gibt uns die Größenordnung der Zahl.

    Bei so vielen signifikanten Bits kann natürlich nicht jede Zahl gespeichert werden. Hier ist die Zahl 1 und die nächsthöhere Zahl nach 1, die das Format speichern kann, 1 + 2-52 ≈ 1.00000000000000022, und der nächsthöhere danach 1 + 2 × 2-52 ≈ 1,00000000000000044:

       +--------------------------------------------------------------- sign bit
      / +-------+------------------------------------------------------ exponent
     / /        |  +-------------------------------------------------+- significand
    / /         "https://stackoverflow.com/"
    0 01111111111 0000000000000000000000000000000000000000000000000000
                    = 1
    0 01111111111 0000000000000000000000000000000000000000000000000001
                    ≈ 1.00000000000000022
    0 01111111111 0000000000000000000000000000000000000000000000000010
                    ≈ 1.00000000000000044
    

    Beachten Sie den Sprung von 1,000000000000000022 auf 1,00000000000000044; Es gibt keine Möglichkeit, 1.0000000000000003 zu speichern. Das kann auch mit ganzen Zahlen passieren: Number.MAX_SAFE_INTEGER (9.007.199.254.740.991) ist der höchste positive ganzzahlige Wert, den das Format aufnehmen kann i und i + 1 sind beide exakt darstellbar (spez). Sowohl 9.007.199.254.740.991 als auch 9.007.199.254.740.992 können dargestellt werden, aber die nächste ganze Zahl, 9.007.199.254.740.993, kann nicht; Die nächste ganze Zahl, die wir nach 9.007.199.254.740.992 darstellen können, ist 9.007.199.254.740.994. Hier sind die Bitmuster, beachten Sie das Bit ganz rechts (niederwertig):

       +--------------------------------------------------------------- sign bit
      / +-------+------------------------------------------------------ exponent
     / /        |  +-------------------------------------------------+- significand
    / /         "https://stackoverflow.com/"
    0 10000110011 1111111111111111111111111111111111111111111111111111
                    = 9007199254740991 (Number.MAX_SAFE_INTEGER)
    0 10000110100 0000000000000000000000000000000000000000000000000000
                    = 9007199254740992 (Number.MAX_SAFE_INTEGER + 1)
    x xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                      9007199254740993 (Number.MAX_SAFE_INTEGER + 2) can't be stored
    0 10000110100 0000000000000000000000000000000000000000000000000001
                    = 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)
    

    Denken Sie daran, dass das Format die Basis 2 ist und mit diesem Exponenten das niederwertigste Bit nicht mehr gebrochen ist; es hat einen Wert von 2. Es kann aus (9.007.199.254.740.992) oder ein (9.007.199.254.740.994) sein; An diesem Punkt haben wir also begonnen, die Genauigkeit sogar auf der ganzen Zahlenskala (ganzzahlig) zu verlieren. Was Auswirkungen auf unsere Schleife hat!

  4. Nach Abschluss der i = 9,007,199,254,740,992 Schleife, i++ gibt uns … i = 9,007,199,254,740,992 wieder; da ist keine änderung drin ida die nächste ganze Zahl nicht gespeichert werden kann und die Berechnung am Ende abgerundet wird. i würde sich ändern, wenn wir es täten i += 2aber i++ kann es nicht ändern. Wir haben also den stationären Zustand erreicht: i ändert sich nie, und die Schleife endet nie.

Hier sind die verschiedenen relevanten Berechnungen:

if (!Number.MAX_SAFE_INTEGER) {
  // Browser doesn't have the Number.MAX_SAFE_INTEGER
  // property; shim it. Should use Object.defineProperty
  // but hey, maybe it's so old it doesn't have that either
  Number.MAX_SAFE_INTEGER = 9007199254740991;
}
var i = 0;
console.log(i, 1/i, 1/i > 0); // 0, Infinity, true
i++;
console.log(i, 1/i, 1/i > 0); // 1, 1, true
// ...eventually i is incremented all the way to Number.MAX_SAFE_INTEGER
i = Number.MAX_SAFE_INTEGER;
console.log(i, 1/i, 1/i > 0); // 9007199254740991 1.1102230246251568e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true (no change)
console.log(i == i + 1);      // true

Benutzer-Avatar
le_m

Antworten:

Die Bedingung 1/i > 0 wird immer als wahr ausgewertet:

  • Zunächst ist es wahr, weil 1/0 wertet zu Infinity und Infinity > 0 ist wahr

  • Es bleibt seitdem wahr 1/i > 0 gilt für alle i < Infinity und i++ nie erreicht Infinity.

Warum tut i++ niemals erreichen Infinity? Aufgrund der begrenzten Genauigkeit der Number Datentyp, es gibt einen Wert für den i + 1 == i:

9007199254740992 + 1 == 9007199254740992 // true

Einmal i erreicht diesen Wert (was entspricht Number.MAX_SAFE_INTEGER + 1), es bleibt auch danach gleich i++.

Wir haben also eine Endlosschleife.


Anhang:

Warum ist 9007199254740992 + 1 == 9007199254740992?

JavaScripts Number Datentyp ist eigentlich ein 64-Bit IEEE 754 Float mit doppelter Genauigkeit. Jeder Number wird zerlegt und in drei Teile gespeichert: 1-Bit-Vorzeichen, 11-Bit-Exponent und 52-Bit-Mantisse. Sein Wert ist -1 Schild × Mantisse × 2 Exponent.

Wie ist 9007199254740992 repräsentiert? Wie 1,0 × 2 53oder binär:

Geben Sie hier die Bildbeschreibung ein

Indem wir das niedrigstwertige Bit der Mantisse erhöhen, erhalten wir die nächsthöhere Zahl:

Geben Sie hier die Bildbeschreibung ein

Der Wert dieser Zahl ist 1,00000000000000022… × 2 53 = 9007199254740994

Was bedeutet das? Number kann entweder 900719925474099 sein2 oder 9007199254740994aber nichts dazwischen.

Nun, welches sollen wir wählen, um 900719925474099 darzustellen2 + 1? Das Rundungsregeln nach IEEE 754 Geben Sie die Antwort: 9007199254740992.

  • kurz und korrekt, besser als die derzeit akzeptierte Antwort

    – AlexWien

    15. Juni 2016 um 19:42 Uhr

  • @AlexWien Die akzeptierte Antwort ist eine vom Community-Wiki akzeptierte Antwort.

    – Fulvo

    15. Juni 2016 um 23:26 Uhr

  • Ich weiß nicht, auf den Begriff “Wiki-community-akzeptiert” zu antworten. Was hat das mit Stackoverflow zu tun? Wenn es sich um einen fremden Link handelt, sollte ein Link bereitgestellt werden. Akzeptierte Antworten auf Stackoverflow können sich immer ändern, der Status Akzeptiert ist nicht endgültig.

    – AlexWien

    16. Juni 2016 um 13:32 Uhr


  • “Warum erreicht i++ niemals Unendlich? Aufgrund der begrenzten Genauigkeit des Zahlendatentyps …” <- Sicherlich würde es niemals unendlich erreichen, selbst mit einem Zahlentyp mit unendlicher Genauigkeit. Sie wissen schon, weil Sie nicht bis zählen können unendlich :P

    – Blorgbart

    21. Juni 2016 um 21:06 Uhr

  • @Blorbart Du kann Zählen Sie mit begrenzter Genauigkeit doppelt bis unendlich, Sie müssen nur um eine viel größere Zahl als 1 erhöhen, z for (var i = 0; i < Infinity; i += 1E306);. Aber ich verstehe wo du herkommst 😉

    – le_m

    21. Juni 2016 um 23:00 Uhr

Benutzer-Avatar
Fulvio

Das Number.MAX_SAFE_INTEGER Die Konstante stellt die maximale sichere Ganzzahl in JavaScript dar. Das MAX_SAFE_INTEGER Konstante hat einen Wert von 9007199254740991. Der Grund für diese Zahl ist, dass JavaScript verwendet wird Zahlen im Gleitkommaformat mit doppelter Genauigkeit wie angegeben in IEEE754 und kann nur Zahlen zwischen -(253 – 1) und 253 – 1.

Sicher bezieht sich in diesem Zusammenhang auf die Fähigkeit, ganze Zahlen exakt darzustellen und korrekt zu vergleichen. Zum Beispiel, Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 wird zu bewerten true, was mathematisch falsch ist. Sehen Number.isSafeInteger() für mehr Informationen.

Da MAX_SAFE_INTEGER ist eine statische Eigenschaft von Numberverwenden Sie es immer als Number.MAX_SAFE_INTEGERund nicht als Eigenschaft von a Number Objekt, das Sie erstellt haben.

AKTUALISIEREN:

Jemand in einer Antwort, die gelöscht wurde, erwähnte: i wird niemals die Unendlichkeit erreichen. Einmal erreicht Number.MAX_SAFE_INTEGER, i++ erhöht die Variable nicht mehr. Dies ist in der Tat nicht Korrekt.

@TJ Crowder kommentiert das i = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER; ist false. Aber die nächste Iteration erreicht einen unveränderlichen Zustand, also ist die Antwort im Wesentlichen richtig.

i im Beispiel nie erreicht Infinity.

  • Speziell, 9007199254740992 + 1 ist 9007199254740992.

    – Köbi

    15. Juni 2016 um 6:05 Uhr

  • @ GerardoFurtado Ich würde mir vorstellen, dass es so wäre.

    – Fulvo

    15. Juni 2016 um 6:24 Uhr

  • @ Gerardo Furtado for (var i=0; NaN > 0; i++) { console.log(i); } wird nichts produzieren.

    – Fulvo

    15. Juni 2016 um 6:25 Uhr

  • @ GerardoFurtado: In diesem Fall würde die Schleife anhalten. Der Schleifenkörper würde überhaupt nie eingegeben werden, da der allererste Test (1/i > 0) wäre falsch, denn if i ist 0, 1/i ist NaNund NaN > 0 ist falsch.

    – TJ Crowder

    15. Juni 2016 um 6:52 Uhr

  • @TJCrowder Ich habe meine Antwort aktualisiert. Danke für den Hinweis!

    – Fulvo

    15. Juni 2016 um 7:04 Uhr

1206040cookie-checkStoppt diese ‘for’-Schleife und warum/warum nicht? für (var i=0; 1/i > 0; i++) { }

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy