4 Stimmen

Kann ich erwarten, dass die Werte von Fließkommazahlen, die ich von Literalkonstanten festlege, nach der Zuweisung an andere Variablen unverändert bleiben?

Wenn ich so etwas mache:

float a = 1.5f;
float b = a;

void func(float arg)
{
  if (arg == 1.5f) printf("Du bist großartig!");
}

func(b);

Wird der Text jedes Mal gedruckt (und auf jedem Rechner)?

EDIT

Ich meine, ich bin mir nicht wirklich sicher, ob der Wert irgendwann durch die FPU geht, auch wenn ich keine Berechnungen durchführe, und wenn ja, ob die FPU die binäre Darstellung des Werts ändert. Ich habe irgendwo gelesen, dass die (ungefähren) gleichen Gleitkommawerte mehrere binäre Darstellungen nach IEEE 754 haben können.

4voto

Motti Punkte 104854

Zunächst einmal kann die Zahl 1.5 im Speicher genau gespeichert werden, daher gilt für diesen spezifischen Wert, ja, es wird immer wahr sein.

Allgemeiner denke ich, dass Ungenauigkeiten nur auftreten, wenn Berechnungen durchgeführt werden, wenn Sie nur einen Wert speichern, auch wenn er keine genaue IEEE-Repräsentation hat, wird er immer demselben Wert zugeordnet (also 0.3 == 0.3 auch wenn 0,3 * 3 != 0,9).

3voto

Jonathan Leffler Punkte 694013

In diesem Beispiel hat der Wert 1.5F eine exakte Darstellung in IEEE 754 (und praktisch jeder anderen denkbaren binären oder dezimalen Gleitkommadarstellung), so dass die Antwort fast sicherlich ja sein wird. Es gibt jedoch keine Garantie, und es könnte Compiler geben, die es nicht schaffen, das Ergebnis zu erzielen.

Wenn Sie den Wert auf einen ohne eine exakte binäre Darstellung ändern, wie z.B. 5.1F, ist das Ergebnis keinesfalls garantiert.

Schon in ihrem ausgezeichneten klassischen Buch "The Elements of Programming Style" sagten Kernighan & Plauger:

Ein weiser Programmierer sagte einmal: "Gleitkommazahlen sind wie Sandhaufen; jedes Mal, wenn man einen bewegt, verliert man ein wenig Sand und sammelt ein wenig Schmutz auf". Und nach einigen Berechnungen kann es ziemlich schmutzig werden.

(Das ist einer von zwei Sätzen im Buch, die ich vor vielen Jahren markiert habe1.)

Sie stellen auch fest:

  • 10.0 mal 0.1 ist so gut wie nie 1.0.
  • Vergleichen Sie Gleitkommazahlen nicht nur auf Gleichheit

Diese Beobachtungen wurden 1978 (für die zweite Ausgabe) gemacht, sind aber auch heute noch grundsätzlich gültig.

Wenn die Frage in ihrem extrem eingeschränkten Umfang betrachtet wird, könnte es in Ordnung sein. Wenn sich die Frage jedoch sehr stark ändert, sind Sie wahrscheinlich eher gebissen als nicht, und Sie werden wahrscheinlich früher gebissen als später.


1 Der andere markierte Satz lautet (ohne Aufzählungszeichen):

  • Der Unterprogrammaufruf erlaubt es uns, die Unregelmäßigkeiten in der Argumentenliste zusammenzufassen [...]
  • Das Unterprogramm selbst fasst die Regelmäßigkeiten des Codes zusammen [...]

2voto

mmmmmmmm Punkte 14837

Wenn es in einem bestimmten Zeitpunkt den FPU passiert, kann es auf Optimierungen und Registerverarbeitung durch den Compiler zurückzuführen sein, dass Sie einen FPU-Register mit einem Wert aus dem Speicher vergleichen. Das erste kann eine höhere Präzision als das letztere haben und daher gibt der Vergleich Ihnen ein falsches Ergebnis. Dies kann je nach Compiler, Compileroptionen und der Plattform, auf der Sie arbeiten, variieren.

1voto

Mark Rushakoff Punkte 236626

Hier ist ein (nicht ganz umfassender) Beweis dafür, dass (zumindest in GCC) bei Gleitkommazahlen die Gleichheit garantiert ist.

Python-Code zur Erzeugung der Datei:

print """
#include 

int main()
{
"""
import random
chars = "abcdefghijklmnopqrstuvwxyz"
randoms = [str(random.random()) for _ in xrange(26)]
for c, r in zip(chars, randoms):
    print "float %s = %sf;" % (c, r)

for c, r in zip(chars, randoms):
    print r'if (%s != %sf) { printf("Error!\n"); }' % (c,r)

print """
    return 0;
}
"""

Ausschnitt der generierten Datei:

#include 

int main()
{

float a = 0.199698325654f;
float b = 0.402517512357f;
float c = 0.700489844438f;
float d = 0.699640984356f;
if (a != 0.199698325654f) { printf("Error!\n"); }
if (b != 0.402517512357f) { printf("Error!\n"); }
if (c != 0.700489844438f) { printf("Error!\n"); }
if (d != 0.699640984356f) { printf("Error!\n"); }

    return 0;
}

Und wenn Sie es richtig ausführen, wird nichts auf dem Bildschirm ausgegeben:

$ ./a.out 
$

Aber hier ist der Clou: Wenn Sie das Literal f nach den Gleitkommazahlen in der Gleichheitsprüfung nicht platzieren, schlägt sie jedes Mal fehl! Bei der Zuweisung können Sie das Literal f jedoch problemlos weglassen.

1voto

xtofl Punkte 39285

Ich habe mir die Disassembly des VS2005-Compilers angesehen. Beim Ausführen dieses einfachen Programms stellte ich fest, dass nach float f=.1; die Bedingung f==.1 zu .... FALSCH führte.

EDIT - dies lag daran, dass der Vergleichswert ein double war. Bei Verwendung eines float-Literal (d.h. .1f) führte der Vergleich zu WAHR. Dies gilt auch beim Vergleich von double-Variablen mit double-Literalen.

Ich habe den Quellcode und die Disassembly hier hinzugefügt:

  float f=.1f;
0041363E  fld         dword ptr [__real@3dcccccd (415764h)] 
00413644  fstp        dword ptr [f] 

  double d=.1;
00413647  fld         qword ptr [__real@3fb999999999999a (415748h)] 
0041364D  fstp        qword ptr [d] 

  bool ffequal = f == .1f;
00413650  fld         dword ptr [f] 
00413653  fcomp       qword ptr [__real@3fb99999a0000000 (415758h)] 
00413659  fnstsw      ax   
0041365B  test        ah,44h 
0041365E  jp          main+4Ch (41366Ch) 
00413660  mov         dword ptr [ebp-0F8h],1 
0041366A  jmp         main+56h (413676h) 
0041366C  mov         dword ptr [ebp-0F8h],0 
00413676  mov         al,byte ptr [ebp-0F8h] 
0041367C  mov         byte ptr [fequal],al 
// hier ist ffequal wahr

  bool dfequal = d == .1f;
0041367F  fld         qword ptr [__real@3fb99999a0000000 (415758h)] 
00413685  fcomp       qword ptr [d] 
00413688  fnstsw      ax   
0041368A  test        ah,44h 
0041368D  jp          main+7Bh (41369Bh) 
0041368F  mov         dword ptr [ebp-0F8h],1 
00413699  jmp         main+85h (4136A5h) 
0041369B  mov         dword ptr [ebp-0F8h],0 
004136A5  mov         al,byte ptr [ebp-0F8h] 
004136AB  mov         byte ptr [dequal],al 
// hier ist dfequal falsch

  bool ddequal = d == .1;
004136AE  fld         qword ptr [__real@3fb999999999999a (415748h)] 
004136B4  fcomp       qword ptr [d] 
004136B7  fnstsw      ax   
004136B9  test        ah,44h 
004136BC  jp          main+0AAh (4136CAh) 
004136BE  mov         dword ptr [ebp-104h],1 
004136C8  jmp         main+0B4h (4136D4h) 
004136CA  mov         dword ptr [ebp-104h],0 
004136D4  mov         al,byte ptr [ebp-104h] 
004136DA  mov         byte ptr [ddequal],al 
// ddequal ist wahr

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X