Frage Warum hört eine float-Variable auf, bei 16777216 in C # inkrementiert zu werden?


float a = 0;
while (true)
{
    a++;
    if (a > 16777216)
        break; // Will never break... a stops at 16777216
}

Kann mir jemand erklären, warum ein Float-Wert in diesem Code bei 16777216 aufhört zu steigen?

Bearbeiten:

Oder noch einfacher:

float a = 16777217; // a becomes 16777216

44
2017-09-26 07:29


Ursprung


Antworten:


Kurze Zusammenfassung von IEEE-754 Gleitkommazahlen (32 Bit) von oben:

  • 1-Bit-Zeichen (0 bedeutet positive Zahl, 1 bedeutet negative Zahl)
  • 8 Bit Exponent (mit -127 Bias, hier nicht wichtig)
  • 23 Bits "Mantisse"
  • Mit Ausnahmen für die Exponentenwerte 0 und 255 können Sie den Wert wie folgt berechnen: (sign ? -1 : +1) * 2^exponent * (1.0 + mantissa)
    • Die Mantissen-Bits repräsentieren binär Ziffern nach das Dezimaltrennzeichen, z.B. 1001 0000 0000 0000 0000 000 = 2^-1 + 2^-4 = .5 + .0625 = .5625 und der Wert vor dem Dezimaltrennzeichen wird nicht gespeichert, sondern implizit als 1 angenommen (wenn der Exponent 255 ist, wird 0 angenommen, aber das ist hier nicht wichtig), so repräsentiert beispielsweise für einen Exponenten von 30 dieses Mantissenbeispiel den Wert 1.5625

Nun zu deinem Beispiel:

16777216 ist genau 224, und würde als 32-Bit-Float wie folgt dargestellt werden:

  • Vorzeichen = 0 (positive Zahl)
  • Exponent = 24 (gespeichert als 24 + 127 = 151 =10010111)
  • Mantisse = .0
  • Als 32-Bit Gleitkommadarstellung: 0 10010111 00000000000000000000000
  • Daher: Wert = (+1) * 2^24 * (1.0 + .0) = 2^24 = 16777216

Schauen wir uns nun die Nummer 16777217 oder genau 2 an24+1:

  • Vorzeichen und Exponent sind gleich
  • Mantisse müsste genau 2 sein-24 damit (+1) * 2^24 * (1.0 + 2^-24) = 2^24 + 1 = 16777217
  • Und hier ist das Problem. Die Mantisse kann nicht den Wert 2 haben-24 Da es nur 23 Bits hat, kann die Zahl 16777217 nicht mit der Genauigkeit von 32-Bit Gleitkommazahlen dargestellt werden!

48
2017-09-26 09:15



16777217 kann nicht exakt mit einem Float dargestellt werden. Die nächsthöhere Zahl, die ein Float genau darstellen kann, ist 16777218.

Sie versuchen also, den Gleitkommawert 16777216 auf 16777217 zu erhöhen, der nicht in einem Float dargestellt werden kann.


11
2017-09-26 07:33



Wenn Sie sich diesen Wert in seiner Binärdarstellung ansehen, werden Sie sehen, dass es sich um eine und viele Nullen handelt 1 0000 0000 0000 0000 0000 0000oder genau 2 ^ 24. Das heißt, bei 16777216 ist die Zahl gerade um eine Ziffer gewachsen.

Da es sich um eine Gleitkommazahl handelt, kann dies bedeuten, dass die letzte Ziffer an ihrem Ende, die noch gespeichert ist (d. H. Innerhalb ihrer Genauigkeit), ebenfalls nach links verschoben wird.

Wahrscheinlich ist das, was Sie sehen, dass die letzte Stelle der Genauigkeit sich gerade auf etwas Größeres als Eins verschoben hat, so dass das Hinzufügen von Eins keinen Unterschied mehr macht.


11
2017-09-26 07:34



Stellen Sie sich das in dezimaler Form vor. Angenommen, Sie hätten die Nummer:

1.000000 * 10^6

oder 1.000.000. Wenn Sie nur sechs Ziffern Genauigkeit hätten, würde das Hinzufügen von 0,5 zu dieser Zahl ergeben

1.0000005 * 10^6

Das aktuelle Denken mit den fp-Rundungsmodi besteht jedoch darin, "Round to Even" anstatt "Round to Nearest" zu verwenden. In diesem Fall wird jedes Mal, wenn Sie diesen Wert erhöhen, er in der Gleitkommaeinheit auf 16.777.216 oder 2 ^ 24 zurückgerundet. Singles in IEE 754 wird dargestellt als:

+/- exponent (1.) fraction

wo die "1" ist implizit und der Bruchteil ist weitere 23 Bits, alle Nullen, in diesem Fall. Die zusätzliche binäre 1 wird in die Schutzziffer übergehen, zum Rundungsschritt hinuntertragen und jedes Mal gelöscht werden, egal wie oft sie erhöht wird. Das ulpoder Einheit an der letzten Stelle wird immer Null sein. Das letzte erfolgreiche Inkrement stammt von:

+2^23 * (+1.) 11111111111111111111111 -> +2^24 * (1.) 00000000000000000000000

2
2017-11-03 02:13