Einzelnen Beitrag anzeigen

Medium

Registriert seit: 23. Jan 2008
3.686 Beiträge
 
Delphi 2007 Enterprise
 
#21

Re: Problem mit RoundTo((100*0.01),0)

  Alt 22. Apr 2008, 10:30
Zitat von peschai:
@Medium
Hast du vorher "SetRoundMode(rmTruncate);" gesetzt ?
Nope, den hab ich natürlich vergessen. Mit diesem erhalte ich auch 100, 1, 0. Das Problem ist das folgende:

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  p7,staedte : real;
begin
  staedte := 100;
  p7 := RoundTo((staedte*0.01),0);
  showmessage(floattostr(staedte));
  showmessage(floattostr(RoundTo((100*0.01),0)));
  showmessage(floattostr(p7));
end;
In dem 2. ShowMessage wird 100*0.01 gerundet, während aber p7 den Wert staedte*0.01 bekommt. Der Knackpunkt ist, dass Delphi konstante Ausdrücke in einem anderen RoundMode berechnet, als unser Programm zur Laufzeit. Ich habe um das zu prüfen die RoundTo Funktion kopiert, und den Parameter AValue in ein array of Byte geschoben, und dann byteweise ausgegeben. Resultat: Die zwei Aufrufe führen zu jeweils unterschiedlichen Darstellungen der 1 binär gesehen. Mit Umwandlung in Strings nicht zu entdecken, da beide Werte durchaus korrekt 1 sind, aber für die FPU beim Runden in diesem Modus einen Unterschied machen!

Das Problem ist letztlich das Setzen des RoundModes selbst, da dies offenbar nicht nur die Rundung betrifft, sondern auch normale Oparationen. Das ist wenn überhaupt also ein Fehler in der FPU, wenn man es aber weiss kann man es umgehen: Einfach den RoundMode als letzten Aufruf vor Round tätigen!

Zur Verdeutlichung:
Delphi-Quellcode:
// So war es bisher, nur die Rechnung auf einzelne Schritte gesplitted
procedure TForm1.Button1Click(Sender: TObject);
var
  p7 : Extended;
  staedte: Extended;
  a1: Extended;
begin
  SetRoundMode(rmTruncate);
  staedte := 100;
  a1 := staedte*0.01;
  p7 := RoundTo(a1,0);
  showmessage(floattostr(staedte));
  showmessage(floattostr(RoundTo((100*0.01),0)));
  showmessage(floattostr(p7));
end;
Delphi-Quellcode:
// Mit dem RoundMode genau vor dem RoundTo gesetzt, geht es auf einmal!
// a1 ist in beiden Fällen zwar 1, allerdings führen die obere und diese
// Versionen zu unterschiedlichen Darstellungen von 1 in a1.
procedure TForm1.Button1Click(Sender: TObject);
var
  p7 : Extended;
  staedte: Extended;
  a1: Extended;
begin
  staedte := 100;
  a1 := staedte*0.01;
  SetRoundMode(rmTruncate);
  p7 := RoundTo(a1,0);
  showmessage(floattostr(staedte));
  showmessage(floattostr(RoundTo((100*0.01),0)));
  showmessage(floattostr(p7));
end;

Tja, und damit lande ich wieder bei meiner ursprünglichen Aussage: Kenne deine Materie...


Edit:
Ein weiterer Effekt dadurch tritt auch auf, wenn man trotz aller Warnungen einen Vergleich per "=" ausführt.
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  a, b, c: Extended;
begin
  a := 100;
  b := a*0.01;
  c := 1;
  if b=c then
    ShowMessage('b=c');
  if SameValue(b, c) then
    ShowMessage('SameValue(b, c)');
end;
Beide Messages werden angezeigt (was allerdings eher Glück als normal ist), fügt man aber nun an den Anfang der Prozedur ein SetRoundMode(rmTruncate) ein, so zeigt Delphi beim Debuggen immer noch "1" für beide Variablen b und c an, aber das erste ShowMessage tritt nicht ein! Selber Effekt wie oben, mit hübschen Auswirkungen. Man stelle sich ein Programm vor, bei dem nachträglich der RoundMode geändert wird, und viel mit direkten Float-Vergleich gearbeitet wird. (Wobei der Programmiere dann nun schon 2 Kardinalfehler gemacht hätte: RoundMode global für das gesamte Programm setzen, und viel schlimmer die direkten Vergleiche.)
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat