Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Plötzlich auftretenes Rundungsproblem (https://www.delphipraxis.net/36065-ploetzlich-auftretenes-rundungsproblem.html)

Sourcemaker 15. Dez 2004 12:17


Plötzlich auftretenes Rundungsproblem
 
Hallo,

ich bin gestern von einem Kunden über eine Rundungsproblematik hingewiesen worden.
Das Problem tritt bei den Funktionen FormatFloat und Str auf. Eine Berechnung die im
vorliegenden Fall 9,32 EUR ergeben müsste 3,45*3/0,9=9,315 wird durch die o.g. Funktionen
als 9,31 EUR ausgegeben.
Beim Kunden trat das Problem aus verschiedenen Rechnern auf d.h. einer zeigte richtig an
ein anderer falsch so dass ich erst an die Rechner dachte dann konnte ich das Problem auch
an meinen Entwicklungsrechner provozieren. Das Problem trat zumindestens nachvollziehbar auf
nachdem das Programm über Indy eine FTP-Verbindung machte. Beim Kunden jedoch wird dieses nicht ausgeführt so das ich davon ausgehe das es noch andere Faktoren geben muß.

Hat jemand etwas ähnliches erlebt bzw. weiß jemand wie sich FormatFloat und Str in
ihren Rundungsverhalten beeinflussen lassen.

Grüße

Frank

freak4fun 15. Dez 2004 12:20

Re: Plötzlich auftretenes Rundungsproblem
 
Das könnte an den Systemeinstellungen liegen, so wie das Datum und die Währungseinheiten.

mfg
freak

Sourcemaker 15. Dez 2004 13:16

Re: Plötzlich auftretenes Rundungsproblem
 
Welche Systemeinstellung ?

Die für mich relevanten setzte ich beim Programmstart z.B.:

Delphi-Quellcode:
   Application.UpdateFormatSettings := False;
   CurrencyDecimals := 2;
   DateSeparator    := '.';
   DecimalSeparator := ',';
   LongDateFormat   := 'dd.mm.yyyy';
   LongTimeFormat   := 'HH:MM:SS';
   ShortDateFormat  := 'dd.mm.yy';
   ShortTimeFormat  := 'HH:MM';
   ThousandSeparator := '.';
   TimeSeparator    := ':';
Grüße

Frank

Jelly 15. Dez 2004 13:20

Re: Plötzlich auftretenes Rundungsproblem
 
Zitat:

Zitat von Sourcemaker
3,45*3/0,9=9,315

:gruebel: :gruebel:
Wie kommst du da auf 9,315?
Was hältst du von 11,5

Luckie 15. Dez 2004 13:22

Re: Plötzlich auftretenes Rundungsproblem
 
Ich will ja nichts sagen, aber ich bekomme da 11,5 raus. :roll:

freak4fun 15. Dez 2004 13:38

Re: Plötzlich auftretenes Rundungsproblem
 
Und wo steht da das bei 5 aufgerundet wird? :gruebel:

mfg
freak

s.h.a.r.k 15. Dez 2004 13:38

Re: Plötzlich auftretenes Rundungsproblem
 
Hey!

Das is ja im Prinzip egal! Interessant ist ja nur wie manche PCs auf 9.31 kommen und andere auf 9.32 - Schon mal mit der folgenden Procedure probiert: (Kenn ich halt von Delphi 7 - weiß net ob es die in Pascal auch gibt :roll: ):
Code:
i:=RoundTo(9.315, -2);
PS: Bei mir liefert das aber auch 9.31... Keine Ahnung warum! Oder du musch dir halt deine eigene Routine schreiben... Wird dir wohl nix anderes übrig bleiben - aber dann macht das auch sicher jeder Rechner gleich :???:

mfg shark

ibp 15. Dez 2004 13:43

Re: Plötzlich auftretenes Rundungsproblem
 
prozentrechnen will gelernt sein...

Code:
3,45*3*0,9=9,315=3,45*3/1,01

Luckie 15. Dez 2004 13:47

Re: Plötzlich auftretenes Rundungsproblem
 
Zitat:

RoundTo uses “Banker’s Rounding”
Wie runden denn "Bänker"?

sakura 15. Dez 2004 13:49

Re: Plötzlich auftretenes Rundungsproblem
 
Zitat:

Zitat von Luckie
Wie runden denn "Bänker"?

X = Wert in Euro,Cent

Trunc(X + 0,005) => gerundeter Wert.

...:cat:...

shmia 15. Dez 2004 13:49

Re: Plötzlich auftretenes Rundungsproblem
 
Zitat:

Zitat von Sourcemaker
ich bin gestern von einem Kunden über eine Rundungsproblematik hingewiesen worden.
Das Problem tritt bei den Funktionen FormatFloat und Str auf. Eine Berechnung die im
vorliegenden Fall 9,32 EUR ergeben müsste 3,45*3/0,9=9,315 wird durch die o.g. Funktionen
als 9,31 EUR ausgegeben.

Es gibt da zwei verschiedene Rundungsverfahren:
* Kaufmänisches Runden: bei x.5 wird immer aufgerundet
* "Banker's rounding": bei x.5 wird abwechselnd auf- und abgerundet

Delphi verwendet "Banker's rounding". Die Auf- und Abrundungen sollen sich in der Masse gegenseitig aufheben.
Kaufmännisches Runden geht so:
Delphi-Quellcode:
function CommercialRound(const X: Extended): Int64;
begin
  Result := Trunc(X);
  if Frac(Abs(X)) >= 0.5 then
    Result := Result + Sgn(X);
end;
Die Art & Weise, mit der die Funktion Round() arbeitet, hängt übrigens von der aktuellen
Einstellung des FPU Flags-Registers ab.
(Wahrscheinlich die Ursache für unterschiedlichem Verhalten von Round auf versch. Rechnern)

Jelly 15. Dez 2004 13:50

Re: Plötzlich auftretenes Rundungsproblem
 
Zitat:

Zitat von Luckie
Wie runden denn "Bänker"?

Immer zu Ihren Gunsten :zwinker:


Das ganze Problem hängt wohl mit der Floatarithmetik zusammen. In was für einen Datentyp wird denn 3,45*3/0,9 gespeichert... Probier doch mal double oder extended, und kuck was dann bei raus kommt.

ibp 15. Dez 2004 13:56

Re: Plötzlich auftretenes Rundungsproblem
 
Die Funktionsweise von RoundTo kann mit der Prozedur Set8087CW oder der Funktion SetRoundMode gesteuert werden :warn:

dizzy 15. Dez 2004 15:43

Re: Plötzlich auftretenes Rundungsproblem
 
Zitat:

Zitat von Jelly
Das ganze Problem hängt wohl mit der Floatarithmetik zusammen. In was für einen Datentyp wird denn 3,45*3/0,9 gespeichert... Probier doch mal double oder extended, und kuck was dann bei raus kommt.

Genau dort würde ich den "Fehler" (es ist eigentlich nur eine Ungenauigkeit) vermuten. Ich hatte hier mal was zur Speicherung von Fließkommazahlen geschrieben - das erklärt evtl. das Verhalten.

Es kann nämlich durchaus sein, dass du 9.315 hinschreibst, aber gerechnet wird (aus technischen Gründen) u.U. mit soetwas wie 9.314999999956. Dann stimmte das Abrunden wieder.

Der Typ "Currency" könnte imho hilfreich bei der Problemlösung sein.

Gruss,
Fabian

Sourcemaker 16. Dez 2004 12:40

Re: Plötzlich auftretenes Rundungsproblem
 
Tut mit leid das ich mich erst jetzt wieder einschalte war total im Stress.

Dir Rechnung sollte lauten 3,45*3*0,9=9,315 anstatt 3,45*3/0,9=9,315. Sorry.

Ich konnte mittlerweile die Sache aufklären:
Ich rufe an bestimmten Programmpunkten eine externe DLL auf die in CaVo (Visual Object)
geschrieben ist und ich rufe nicht FormatFloat direkt auf sondern eine Funktion:

Delphi-Quellcode:
function FormStr(const v: Variant; nLen: integer; nDec: integer = 0): string;
var
   cFormat : string;
procedure MakeFormat;
begin
   if nLen > 3 then
      cFormat := '#,'+cFormat;
end;
begin
   if nDec = 0 then
   begin
      cFormat := '##0';
      MakeFormat;
   end
   else
   begin
      cFormat := '##0.'+Replicate('0',nDec);
      nLen   := nLen-nDec-1;
      MakeFormat;
   end;
   Result := FormatFloat(cFormat,v);
end;
Diese Funktion bekommt den Zahlenwert als Variant übertragen und funktioniert solange ich nicht
die besagte DLL aufgerufen habe. Ich habe noch eine weitere ähnliche Funktion die mit Str arbeitet
wo es zum gleichen Problem kommt.
Die Lösung liegt zum einen in der Verwendung von Currency-Variablen (wo es möglich ist) zum anderen auf die angesprochenen Funktionen bezogen auf zusätzliche Rundungsfunktionen.
Delphi-Quellcode:
function FormStr(v: Variant; nLen: integer; nDec: integer = 0): string;
var
   cFormat : string;
procedure MakeFormat;
begin
   if nLen > 3 then
      cFormat := '#,'+cFormat;
end;
begin
   if nDec = 0 then
   begin
      cFormat := '##0';
      MakeFormat;
   end
   else
   begin
      cFormat := '##0.'+Replicate('0',nDec);
      nLen   := nLen-nDec-1;
      MakeFormat;
   end;
   case VarType(v) of
   varCurrency : v := MyRoundC(v,nDec);
   varInteger : ;
   else v := MyRound(v,nDec);
   end;
   Result := FormatFloat(cFormat,v);
end;

function MyRoundC(nZahl:Currency;nDec:integer):Currency;
var
  i: integer;
begin
  for i := 1 to nDec do nZahl := nZahl * 10;
  nZahl := Int(nZahl + iif(nZahl > 0, 0.5, -0.5));
  for i := 1 to nDec do nZahl := nZahl / 10;
  Result := nZahl;
end;
Wie gesagt damit löse ich die Probleme aber interessieren würde es mich doch was die ext. DLL
mit den Variants anstellt. Falls also jemand eine Idee hat ...
Ist die Round-Funktion Ok oder gibt es bessere Lösungen ?

Grüße

Frank


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:36 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz