Frage

Der folgende Code in C # (.NET 3.5 SP1) ist eine Endlosschleife auf meinem Rechner:

for (float i = 0; i < float.MaxValue; i++) ;

Es erreichte die Zahl 16.777.216,0 und 16.777.216,0 + 1 ausgewertet 16.777.216,0. Doch an dieser Stelle:. I + 1 = i

Das ist etwas Verrücktheit.

Ich weiß, gibt es einige Ungenauigkeiten in wie Gleitkommazahlen gespeichert werden. Und ich habe gelesen, dass ganze Zahlen größer 2 ^ 24 als nicht richtig als Schwimmer gespeichert werden.

immer noch über den Code, sollte auch in C # gültig, wenn die Zahl nicht richtig dargestellt werden.

Warum funktioniert es nicht?

Sie können das gleiche bekommen für Doppel passieren, aber es dauert eine sehr lange Zeit. 9007199254740992,0 ist die Grenze für Doppel.

War es hilfreich?

Lösung

Richtig, so ist das Problem, dass, um eine mit dem Schwimmer hinzuzufügen, wäre es zu werden hat

16777217.0

Es passiert einfach so, dass dies für die radix an einer Grenze ist und nicht gerade als Schwimmer dargestellt werden kann. (Der nächste höchste Wert vorhanden ist 16777218.0)

Also, es rundet auf den nächsten darstellbaren float

16777216.0

Lassen Sie es mich so sagen:

Da Sie haben eine Floating Menge an Präzision, müssen Sie durch eine höhere und höhere Zahl erhöht werden.

EDIT:

Ok, das ist ein bisschen schwierig zu erklären, aber versuchen Sie dies:

float f = float.MaxValue;
f -= 1.0f;
Debug.Assert(f == float.MaxValue);

Das läuft ganz gut, weil auf diesem Wert, um eine Differenz von 1.0f darzustellen, müßten Sie über 128 Bit Genauigkeit. Ein Schwimmer hat nur 32 Bit.

EDIT2

Nach meinen Berechnungen mindestens 128 Binärzahlen unsigned notwendig wäre.

log(3.40282347E+38) * log(10) / log(2) = 128

Als eine Lösung für Ihr Problem, Sie könnte Schleife durch zwei 128-Bit-Zahlen. Dies wird jedoch mindestens ein Jahrzehnt in Anspruch nehmen.

Andere Tipps

Stellen Sie sich zum Beispiel, dass eine Fließkommazahl um bis zu zwei signifikanten Dezimalstellen dargestellt wird, sowie ein Exponent: in diesem Fall Sie 0-99 genau zählen können. Die nächste wäre 100, sondern weil Sie nur zwei signifikante Stellen haben können, die als „1,0 mal 10 hoch 2“ gespeichert würden. Hinzufügen von ein bis das wäre ... was?

Am besten wäre es 101 als Zwischenergebnis sein, die tatsächlich (über einen Rundungsfehler, die die unbedeutende 3. Stelle verwirft) gespeichert würden als „1,0 mal 10 hoch 2“ wieder.

Um zu verstehen, was falsch läuft Sie gehen zu den IEEE-Standard auf Gleitkomma haben zu lesen

Lassen Sie uns die Struktur eines Gleitkomma Nummer für eine Sekunde:

Eine Gleitkommazahl ist in zwei Teile gebrochen (ok 3, aber das Vorzeichenbit für einen zweiten ignorieren).

Sie haben einen Exponenten und eine Mantisse. Wie so:

smmmmmmmmeeeeeee

. Hinweis: das ist auf die Anzahl der Bits nicht acurate, aber es gibt Ihnen eine allgemeine Vorstellung davon, was passiert

Um herauszufinden, welche Nummer Sie haben wir die folgende Berechnung:

mmmmmm * 2^(eeeeee) * (-1)^s

Also, was ist float.MaxValue sein würde? Nun werde ich die größtmöglichen Mantisse und den größtmöglichen Exponenten haben. Nehmen wir an, das sieht so etwas wie:

01111111111111111

in Wirklichkeit definieren wir NAN und + -INF und ein paar andere Konventionen, aber sie eine Sekunde lang ignorieren, weil sie auf Ihre Frage nicht relevant sind.

Also, was passiert, wenn Sie 9.9999*2^99 + 1 haben? Nun, Sie haben nicht genug signifikante Zahlen hinzufügen 1. Als Ergebnis es auf die gleiche Zahl gerundet wird. Im Fall von einzelner Gleitkommagenauigkeit bei dem der Punkt +1 beginnt geschieht werden abgerundet werden 16777216.0

Es hat nichts mit Überlauf zu tun, oder in der Nähe des Maximalwertes ist. Der Schwimmer Wert für 16.777.216,0 hat eine binäre Darstellung 16777216 Sie es dann um 1 erhöht, so sollte es sein 16.777.217,0, mit der Ausnahme, dass die binäre Darstellung von 16.777.217,0 ist 16777216 !!! So ist es nicht wirklich erhöht bekommen oder zumindest nicht der Zuwachs nicht tun, was Sie erwarten.

Hier ist eine Klasse von Jon Skeet geschrieben, dies zeigt:

DoubleConverter.cs

Versuchen Sie diesen Code mit ihm:

double d1 = 16777217.0;
Console.WriteLine(DoubleConverter.ToExactString(d1));

float f1 = 16777216.0f;
Console.WriteLine(DoubleConverter.ToExactString(f1));

float f2 = 16777217.0f;
Console.WriteLine(DoubleConverter.ToExactString(f2));

Beachten Sie, wie die interne Darstellung von 16.777.216,0 ist die gleiche 16.777.217,0 !!

Die Iteration, wenn i nähert sich float.MaxValue i hat knapp unter diesem Wert. Die nächste Iteration fügt i, aber es kann keine Zahl größer als float.MaxValue halten. So hält er einen Wert viel kleiner, und beginnt die Schleife erneut.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top