Frage

float f = 0.7;
if( f == 0.7 )
    printf("equal");
else
    printf("not equal");

Warum ist die Ausgabe not equal ?

Warum passiert das?

War es hilfreich?

Lösung

Dies geschieht, weil in Ihrer Erklärung

  if(f == 0.7)

die 0,7 als Doppel behandelt. Versuchen Sie 0.7f, um sicherzustellen, wird der Wert als float behandelt:

  if(f == 0.7f)

Aber als Michael in den Kommentaren unten Sie vorgeschlagen sollte nie für exakte Gleichheit von Gleitkommazahlen testen.

Andere Tipps

Diese Antwort, die bestehenden ergänzen: beachten Sie, dass 0,7 nicht genau darstellbar entweder als Schwimmer (oder als Doppel). Wenn es genau dargestellt wurde, dann gäbe es keinen Informationsverlust, wenn die Umwandlung zu schweben und dann zu verdoppeln zurück, und man würde dieses Problem nicht.

Es gibt sogar argumentiert werden könnte, dass es Warnung für wörtlich Gleitkommakonstanten ein Compiler sein, die nicht exakt dargestellt werden können, vor allem, wenn der Standard so unscharf darüber, ob die Rundung wird zur Laufzeit im Modus gemacht wird, hat als diese Zeit oder bei der Kompilierung-Zeit in einem anderen Rundungsmodus eingestellt.

Alle Nicht-Integer-Zahlen, die genau 5 als letzten Nachkommastelle haben dargestellt werden können. Leider ist das Gegenteil nicht wahr: einige Zahlen als letzte Nachkommastelle 5 haben und können nicht genau dargestellt werden. Kleine Zahlen können alle genau dargestellt werden, und die Division durch eine Potenz von 2 wandelt eine Zahl, die in eine andere dargestellt werden kann, die dargestellt werden können, solange man das Reich der normalisierte Zahlen nicht eingegeben werden.

Schauen wir uns zunächst die Float-Nummer an.Ich nehme 0,1f, es ist 4 Byte lang (binär32), im Hexadezimalformat ist es das
3D CC CC CD.
Um es in eine Dezimalzahl umzuwandeln, müssen wir nach dem Standard IEEE 754 wie folgt vorgehen:

enter image description here
Im binären 3D ist CC CC CD
0 01111011 1001100 11001100 11001101
Hier ist die erste Ziffer ein Vorzeichenbit.0 bedeutet (-1)^0, dass unsere Zahl positiv ist.
Die zweiten 8 Bits sind ein Exponent.Binär ist es 01111011 – dezimal 123.Aber der wahre Exponent ist 123-127(immer 127)=-4, das heißt, wir müssen die Zahl, die wir erhalten, mit 2^(-4) multiplizieren.
Die letzten 23 Bytes sind die Signifikantengenauigkeit.Dort multiplizieren wir das erste Bit mit 1/(2^1) (0,5), das zweite mit 1/(2^2) (0,25) und so weiter.Folgendes bekommen wir:


enter image description here enter image description here

Wir müssen alle Zahlen (Potenz von 2) addieren und 1 hinzufügen (standardmäßig immer 1).Es ist
1,60000002384185791015625
Jetzt multiplizieren wir diese Zahl mit 2^(-4), sie stammt von Exponent.Wir teilen einfach die obige Zahl viermal durch 2:
0,100000001490116119384765625
Ich habe den MS-Rechner verwendet


**

Jetzt der zweite Teil.Konvertieren von Dezimalzahl in Binärzahl.

**
Ich nehme die Zahl 0,1
Es ist einfacher, weil es keinen ganzzahligen Teil gibt.Erstes Vorzeichenbit – es ist 0.Exponenten- und Signifikantengenauigkeit werde ich jetzt berechnen.Die Logik besteht darin, mit 2 ganzen Zahlen (0,1*2=0,2) zu multiplizieren und, wenn sie größer als 1 ist, zu subtrahieren und fortzufahren.
enter image description here
Und die Zahl ist .00011001100110011001100110011. Der Standard besagt, dass wir nach links verschieben müssen, bevor wir 1 (etwas) erhalten.Wie Sie sehen, benötigen wir, ausgehend von dieser Zahl, 4 Schichten Exponent(127-4=123).Und die Significand-Präzision ist es jetzt
10011001100110011001100(und es gibt verlorene Bits).
Nun die ganze Zahl.Vorzeichenbit 0 Exponent ist 123 (01111011) und Signifikantenpräzision ist 10011001100110011001100 und ganz ist es
00111101110011001100110011001100 Vergleichen wir es mit denen aus dem vorherigen Kapitel
00111101110011001100110011001101
Wie Sie sehen, sind die letzten Bits nicht gleich.Das liegt daran, dass ich die Zahl gekürzt habe.Die CPU und der Compiler wissen, dass die Genauigkeit nach Significand nicht aufrechterhalten werden kann, und setzen einfach das letzte Bit auf 1.

Das Problem ist, Sie konfrontiert, wie andere Kommentatoren festgestellt haben, dass es in der Regel nicht sicher ist für die exakte Äquivalenz zwischen Schwimmern zu testen, wie Initialisierung Fehler oder Fehler in den Berechnungen Rundung können geringfügige Unterschiede einführen, die den Operator == verursachen return false.

Eine bessere Praxis ist so etwas wie

zu tun
float f = 0.7;
if( fabs(f - 0.7) < FLT_EPSILON )
    printf("equal");
else
    printf("not equal");

Unter der Annahme, dass FLT_EPSILON hat als entsprechend kleinen Schwimmer Wert für Ihre Plattform definiert.

Da die Rundung oder Initialisierung Fehler unwahrscheinlich sein wird, den Wert von FLT_EPSILON überschreiten, dies wird Ihnen die zuverlässige Äquivalenztest für Sie suchen.

Viele der Antworten rund um das Internet des Blicks auf der abosulute Differenz zwischen Gleitkommazahlen die Fehler machen, dies gilt nur für Sonderfälle, die robuste Art und Weise ist, wie in unten in der relativen Differenz aussehen:

      // Floating point comparison:

        bool CheckFP32Equal(float referenceValue, float value)
        {
           const float fp32_epsilon = float(1E-7);
           float abs_diff = std::abs(referenceValue - value);

           // Both identical zero is a special case
           if( referenceValue==0.0f && value == 0.0f)
              return true;

           float rel_diff = abs_diff / std::max(std::abs(referenceValue) , std::abs(value) ); 

           if(rel_diff < fp32_epsilon)
                 return true;
           else 
                 return false;

        }

Eine weitere in der Nähe genaue Frage wurde an diesem verknüpft also die Jahre zu spät Antwort. Ich glaube nicht, dass die oben genannten Antworten sind abgeschlossen.

int fun1 ( void )
{
      float x=0.7;
      if(x==0.7) return(1);
      else       return(0);
}
int fun2 ( void )
{
      float x=1.1;
      if(x==1.1) return(1);
      else       return(0);
}
int fun3 ( void )
{
      float x=1.0;
      if(x==1.0) return(1);
      else       return(0);
}
int fun4 ( void )
{
      float x=0.0;
      if(x==0.0) return(1);
      else       return(0);
}
int fun5 ( void )
{
      float x=0.7;
      if(x==0.7f) return(1);
      else       return(0);
}
float fun10 ( void )
{
    return(0.7);
}
double fun11 ( void )
{
    return(0.7);
}
float fun12 ( void )
{
    return(1.0);
}
double fun13 ( void )
{
    return(1.0);
}

Disassembly of section .text:

00000000 <fun1>:
   0:   e3a00000    mov r0, #0
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e3a00000    mov r0, #0
   c:   e12fff1e    bx  lr

00000010 <fun3>:
  10:   e3a00001    mov r0, #1
  14:   e12fff1e    bx  lr

00000018 <fun4>:
  18:   e3a00001    mov r0, #1
  1c:   e12fff1e    bx  lr

00000020 <fun5>:
  20:   e3a00001    mov r0, #1
  24:   e12fff1e    bx  lr

00000028 <fun10>:
  28:   e59f0000    ldr r0, [pc]    ; 30 <fun10+0x8>
  2c:   e12fff1e    bx  lr
  30:   3f333333    svccc   0x00333333

00000034 <fun11>:
  34:   e28f1004    add r1, pc, #4
  38:   e8910003    ldm r1, {r0, r1}
  3c:   e12fff1e    bx  lr
  40:   66666666    strbtvs r6, [r6], -r6, ror #12
  44:   3fe66666    svccc   0x00e66666

00000048 <fun12>:
  48:   e3a005fe    mov r0, #1065353216 ; 0x3f800000
  4c:   e12fff1e    bx  lr

00000050 <fun13>:
  50:   e3a00000    mov r0, #0
  54:   e59f1000    ldr r1, [pc]    ; 5c <fun13+0xc>
  58:   e12fff1e    bx  lr
  5c:   3ff00000    svccc   0x00f00000  ; IMB

Warum fun3 hat und FUN4 eine Rückkehr und nicht die anderen? warum tut fun5 Arbeit?

Es geht um die Sprache. Die Sprache sagt, dass 0,7 ist eine doppelte, wenn Sie diese Syntax 0.7f verwenden, dann ist es eine einzige. So

  float x=0.7;

Doppel 0,7 auf einen einzigen umgewandelt wird und in x gespeichert.

  if(x==0.7) return(1);

Die Sprache sagt, dass wir auf die höhere Präzision zu fördern haben so in x das einzige zu einem Doppel umgewandelt und im Vergleich mit dem Doppel 0.7.

00000028 <fun10>:
  28:   e59f0000    ldr r0, [pc]    ; 30 <fun10+0x8>
  2c:   e12fff1e    bx  lr
  30:   3f333333    svccc   0x00333333

00000034 <fun11>:
  34:   e28f1004    add r1, pc, #4
  38:   e8910003    ldm r1, {r0, r1}
  3c:   e12fff1e    bx  lr
  40:   66666666    strbtvs r6, [r6], -r6, ror #12
  44:   3fe66666    svccc   0x00e66666

Einzel 3f333333 Doppel 3fe6666666666666

Wie Alexandr wies darauf hin, wenn die Antwort bleibt IEEE 754 ein einziges ist

  

seeeeeeeefffffffffffffffffffffff

Und doppelt so hoch ist

  

seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff

mit 52 Bit von Fraktion anstatt der 23, die einzelne hat.

00111111001100110011... single
001111111110011001100110... double

0 01111110 01100110011... single
0 01111111110 01100110011... double

Genau wie 1/3 in der Basis 10 ist 0,3333333 ... für immer. Wir haben ein sich wiederholendes Muster hier 0110

01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.

Und hier ist die Antwort.

  if(x==0.7) return(1);

x enthält 01100110011001100110011 als seine Fraktion, wenn dieser zurück umgewandelt wird den Anteil zu verdoppeln ist

01100110011001100110011000000000....

, die nicht gleich

ist
01100110011001100110011001100110...

aber hier

  if(x==0.7f) return(1);

das tut die gleichen Bitmuster passieren Förderung werden miteinander verglichen.

Warum 1.0 Arbeit?

00000048 <fun12>:
  48:   e3a005fe    mov r0, #1065353216 ; 0x3f800000
  4c:   e12fff1e    bx  lr

00000050 <fun13>:
  50:   e3a00000    mov r0, #0
  54:   e59f1000    ldr r1, [pc]    ; 5c <fun13+0xc>
  58:   e12fff1e    bx  lr
  5c:   3ff00000    svccc   0x00f00000  ; IMB

0011111110000000...
0011111111110000000...

0 01111111 0000000...
0 01111111111 0000000...

In beiden Fällen ist der Anteil nur Nullen. So Umwandlung von doppelt auf einzelne dort zu verdoppeln ist kein Verlust an Präzision. Er wandelt von einzelnen genau zu verdoppeln und die Bit-Vergleich der beiden Werte funktioniert.

Die höchste abgestimmt und geprüft Antwort von Halfdan ist die richtige Antwort, dies ist ein Fall von Misch Präzision und Sie tun sollten nie ein Vergleich entspricht.

Der Grund ist nicht in dieser Antwort gezeigt. 0,7 versagt 1.0 funktioniert. Warum haben 0,7 scheitern ist nicht gezeigt. Eine doppelte Frage 1.1 nicht als gut.


EDIT

Das Gleichheits kann hier aus dem Problem getroffen wird, ist es eine andere Frage, die bereits beantwortet wurde, aber es ist das gleiche Problem und hat auch den „Was zum ...“ ersten Schock.

int fun1 ( void )
{
      float x=0.7;
      if(x<0.7) return(1);
      else       return(0);
}
int fun2 ( void )
{
      float x=0.6;
      if(x<0.6) return(1);
      else       return(0);
}

Disassembly of section .text:

00000000 <fun1>:
   0:   e3a00001    mov r0, #1
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e3a00000    mov r0, #0
   c:   e12fff1e    bx  lr

Warum zeigt man als kleiner als die andere nicht weniger als? Wann sollte sie gleich sein.

Von oben kennen wir die 0,7 Geschichte.

01100110011001100110011 single, 23 bits
01100110011001100110011001100110.... double 52 bits.

01100110011001100110011000000000....

ist kleiner als.

01100110011001100110011001100110...

0,6 ist ein anderes sich wiederholendes Muster 0011 anstatt 0110.

aber, wenn sie von einem Doppel zu einem einzigen oder in allgemeinen umgewandelt, wenn vertreten als ein einziges IEEE 754.

00110011001100110011001100110011.... double 52 bits.
00110011001100110011001 is NOT the fraction for single
00110011001100110011010 IS the fraction for single

IEEE 754 verwendet Modi Rundung aufrunden, abrunden oder rund Null. Compiler ist in der Regel standardmäßig aufrunden. Wenn Sie in der Grundschule 12345678 Runden erinnern, wenn ich an die 3. Stelle von oben abrunden wollte wäre es 12.300.000, aber rund um die nächste Ziffer 1.235.000, wenn die Ziffer nach 5 oder mehr dann aufrunden. 5 1/2 von 10 die Basis (dezimal) in binären 1 1/2 der Basis ist so, wenn die Ziffer nach der Position, die wir runden möchten 1 dann sonst tun sie nicht aufrunden. Also für 0,7 wir nicht aufrunden, für 0,6 wir aufrunden.

Und jetzt ist es einfach, dass

sehen
00110011001100110011010

zu einem Doppel umgewandelt wegen (x <0,7)

00110011001100110011010000000000....

größer als

00110011001100110011001100110011....

Also, ohne dass über die Verwendung von gleich das Problem noch sprechen präsentiert sich 0,7 Doppel 0.7f ist Single, die Operation in höchster Präzision gefördert wird, wenn sie sich unterscheiden.

Bedenken Sie:

int main()
{
    float a = 0.7;
    if(0.7 > a)
        printf("Hi\n");
    else
        printf("Hello\n");
    return 0;
}

Wenn (0,7> a) hier ein ein float-Variable ist und 0.7 ist ein doppeltes konstant. Der Doppel-Konstante 0.7 größer als der Schwimmer Variable a. Daraus ergibt sich die, wenn die Bedingung erfüllt ist, und er druckt 'Hi'

Beispiel:

int main()
{
    float a=0.7;
    printf("%.10f %.10f\n",0.7, a);
    return 0;
}

Ausgabe:
0,7000000000 0,6999999881

Wenn Sie den Datentyp von ändern F Zu doppelt, es wird gedruckt gleich, Dies liegt daran, dass Konstanten in Gleitkommazahlen gespeichert sind doppelt und nicht schwimmend lang, die doppelte Genauigkeit ist hoch und in float ist ein weniger präziser, doppelter Wert gespeichert 64 Bit-Binär- und Float-Wert gespeichert in 32 bit binär ist, wird es völlig klar, wenn Sie die Methode der Konvertierung von Gleitkommazahlen in eine Binärkonvertierung sehen.

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