Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Genauigkeit von Datentypen (https://www.delphipraxis.net/184097-genauigkeit-von-datentypen.html)

PaddyVII 25. Feb 2015 13:35

Genauigkeit von Datentypen
 
Guten Mittag, Delphianer,

ich denke ich hab hier etwas gefunden, über das nicht nur ich mir den Kopf zerbreche, aber ich möchte es verstehen, deshalb frag ich euch:

Vorarab: Ich wollte einen Münzgeldrechner schreiben (Quellcode ist unten), der einen Betrag, den man eingibt umwandelt in Münzgeld wechselt. Ziel dabei war es, so wenig wie möglich Münzen zu benutzen. Dies war die aufgabenstellung, nun kommt mein dazu passender code


Delphi-Quellcode:

procedure TForm1.edtEingabeChange(Sender: TObject);
var
  EingegebeneZahl: Currency;
  arGeld: array [0 .. 7] of Currency;
  i: Integer;
  zaehler: Integer;
begin
  // Array Füllen
  arGeld[0] := 2;
  arGeld[1] := 1;
  arGeld[2] := 0.50;
  arGeld[3] := 0.20;
  arGeld[4] := 0.10;
  arGeld[5] := 0.05;
  arGeld[6] := 0.02;
  arGeld[7] := 0.01;

  EingegebeneZahl := StrToFloat(edtEingabe.Text);
  mmoLog.Lines.Add(FloatToStr(EingegebeneZahl));

  for i := 0 to 7 do
  begin
    zaehler := 0;
    repeat
      if EingegebeneZahl < arGeld[i] then
      begin
        break;
      end;
      mmoLog.Lines.Add('Operation: '+FloatToStr(EingegebeneZahl)+' - '+FloatToStr(arGeld[i]));
      EingegebeneZahl := EingegebeneZahl - arGeld[i];
      mmoLog.Lines.Add(FloatToStr(EingegebeneZahl));

      Inc(zaehler);
    until arGeld[i] > EingegebeneZahl;
    if arGeld[i] = 2 then
      lbl2.Caption := IntToStr(zaehler);
    if arGeld[i] = 1 then
      lbl1.Caption := IntToStr(zaehler);
    if arGeld[i] = 0.50 then
      lbl050.Caption := IntToStr(zaehler);
    if arGeld[i] = 0.20 then
      lbl020.Caption := IntToStr(zaehler);
    if arGeld[i] = 0.10 then
      lbl010.Caption := IntToStr(zaehler);
    if arGeld[i] = 0.05 then
      lbl005.Caption := IntToStr(zaehler);
    if arGeld[i] = 0.02 then
      lbl002.Caption := IntToStr(zaehler);
    if arGeld[i] = 0.01 then
      lbl001.Caption := IntToStr(zaehler);
  end;
end;


Dieser Code funktioniert, soweit ohne fehler!!


Ändert man jedoch den Datentyp von currency in double, single o.ä...Funkitioniert das Programm nicht mehr richtig!! (Als kleine veranschaulichung habe ich ein memo eingebaut, welches die aktion mitschreibt, die gerade durchgeführt wird). Es wird nach der Dritten bzw. 4 Berechnung alles ungenau, und es wird nicht mehr weiter berechnet.




Ich will wissen warum?!! warum ändert sich der wert von alleine/ wird ungenau...


Ich hoffe ein paar von euch können sich einen Reim darauf machen



LG

Paddy_VII

p.s: es geht nicht darum, ob der Code nun effizient ist, oder nicht :?:?:?

Union 25. Feb 2015 13:45

AW: !!Riesenprobelm mit Datentypen!!
 
Das liegt an der internen Darstellung. Ein Wert 0.5 wird als Double evtl. als 0.5000000128973 gespeichert. Für diese Fälle arbeite mit CompareValue mit einem entsprechenden Delta.

PaddyVII 25. Feb 2015 13:53

AW: !!Riesenprobelm mit Datentypen!!
 
aber warum genau wird der wert ungenau?? wir haben ihn als double angegeben, klar aber dann müsste doch auch 0,5 0,5000000000000000000000000000 werden und nich irgendwelche wahllosen zahlen hintendranhackseln

Daniel 25. Feb 2015 13:56

AW: !!Riesenprobelm mit Datentypen!!
 
Das hängt damit zusammen, dass auch ein moderner Computer Fließkommazahlen nicht exakt darstellen kann.
Schau mal dort:
http://de.wikipedia.org/wiki/Gleitkommazahl

PaddyVII 25. Feb 2015 13:59

AW: !!Riesenprobelm mit Datentypen!!
 
aber dann bedeutet das doch, dass es mit Single double usw. völlig schwachsinnig wäre zu rechnen, weil sie ehh ungenau werden oder?

Daniel 25. Feb 2015 14:03

AW: !!Riesenprobelm mit Datentypen!!
 
Wie genau brauchst Du es denn?
Jeder Datentyp hat eine auf n Stellen garantierte Genauigkeit.
Dann muss man schauen, was man braucht und kann dann den entsprechenden Datentyp wählen.

"Single" ist für viele, viele Fälle absolut ausreichend.

PaddyVII 25. Feb 2015 14:08

AW: !!Riesenprobelm mit Datentypen!!
 
eben, und ich wollte einfach nur mit max. 2 stellen nach dem Komma rechnen... und dann macht er mir sowas....ist das selbst bei soo wenigen stellen (bei mehr als 30 stellen nach dem komma hätt ich jaa verstanden dass er ungenau wird) relevant?? darf doch eigentlich nicht sein, oder?

Daniel 25. Feb 2015 14:13

AW: !!Riesenprobelm mit Datentypen!!
 
Zitat:

Zitat von PaddyVII (Beitrag 1291474)
darf doch eigentlich nicht sein, oder?

Doch. Oder: Leider ja. Oder: Is' so.
Deine Zahlen sind nur auf die ersten n Stellen korrekt - darauf kannst Du Dich verlassen, auf mehr jedoch auch nicht.
Deswegen sollte man Fließkommazahlen auch nicht als Ganzes auf Gleichheit zu irgendeinem anderen Wert prüfen, sondern Funktionen wie "CompareValue" nutzen, die Dir streng genommen nur sagen, ob Wert A "recht dicht" an Wert B ist.

himitsu 25. Feb 2015 14:22

AW: Genauigkeit von Datentypen
 
Zitat:

Ändert man jedoch den Datentyp von currency in double, single o.ä...Funkitioniert das Programm nicht mehr richtig!!
Jupp, das kann darf sein, denn genau aus dem Grund gibt es eben das Currency oder die BCD-Typen.
Und aus selbem Grund darf man reelle Typen niemals per Operator (vorallem
Delphi-Quellcode:
=
) "genau" vergleichen, sondern verwendet z.B. CompareValue, SameValue, IsZero usw.

Zitat:

Deine Zahlen sind nur auf die ersten n Stellen korrekt - darauf kannst Du Dich verlassen, auf mehr jedoch auch nicht.
Auf wieviele Stellen man sich "verlassen" kann, das steht in der Dokumentation, aber danach kann/wird es Abweichungen geben, vorallem dann, wenn sich der Wert nicht ganz genau in einem dualen Zahlensystem darstellen lässt.

Umrechung: dual <> dezimal

Mikkey 25. Feb 2015 14:25

AW: !!Riesenprobelm mit Datentypen!!
 
Zitat:

Zitat von PaddyVII (Beitrag 1291474)
darf doch eigentlich nicht sein, oder?

Für Computer, die nur eine endliche Anzahl Bits für die Repräsentation einer reellen Zahl verwenden, ist das nicht anders möglich. Garantiert ist für Single, Double und Extended lediglich, dass die neutralen Elemente 0 und 1 exakt repräsentiert werden.

Da üblicherweise die Mantisse binär dargestellt wird, kann man sich noch nicht einmal darauf verlassen, dass kleine ganze Zahlen oder Brüche aus kleinen ganzen Zahlen exakt wiedergegeben werden.

mjustin 25. Feb 2015 14:32

AW: !!Riesenprobelm mit Datentypen!!
 
Zitat:

Zitat von Mikkey (Beitrag 1291484)
Da üblicherweise die Mantisse binär dargestellt wird, kann man sich noch nicht einmal darauf verlassen, dass kleine ganze Zahlen oder Brüche aus kleinen ganzen Zahlen exakt wiedergegeben werden.

42 geteilt durch 7 ergibt eine Zahl die kleiner als 10 ist :)

BUG 25. Feb 2015 14:58

AW: Genauigkeit von Datentypen
 
Geld ist ja ein bisschen ein Spezialfall, da es scheinbar gebrochene Werte sind, aber eigentlich gibt es eine diskrete Einheit z.B. 1 Cent.

Bei den meisten physikalischen Größen wie Geschwindigkeit, Masse, usw. hat man oft denn Fall, dass die Eingabewerte eine viel geringere Messgenauigkeit haben als die Ungenauigkeit der Gleitkommazahlen. Ansonsten ist das korrekte Umgehen mit numerische Ungenauigkeiten eine nicht triviale Aufgabe und wird häufig einfach ignoriert :mrgreen:

UliBru 25. Feb 2015 15:56

AW: Genauigkeit von Datentypen
 
Einen netten Überblick zum Thema Genauigkeit von Gleitkommazahlen gibt
https://software.intel.com/sites/def...ol-2012-08.pdf

Das Rechnen mit Geld hat auch Tücken trotz fester kleiner Einheiten, also z.B. 1 Cent. Nämlich dann wenn z.B. Mehrwertsteuer ins Spiel kommt. Wo dann eben wiederum kleinere Werte als die genannten Einheiten auftreten.

PaddyVII 26. Feb 2015 09:53

AW: !!Riesenprobelm mit Datentypen!!
 
Zitat:

Zitat von Union (Beitrag 1291467)
Das liegt an der internen Darstellung. Ein Wert 0.5 wird als Double evtl. als 0.5000000128973 gespeichert. Für diese Fälle arbeite mit CompareValue mit einem entsprechenden Delta.

Wie würde denn das programm mit compareValue aussehen??

DeddyH 26. Feb 2015 10:09

AW: Genauigkeit von Datentypen
 
Zitat:

Delphi-Quellcode:
if arGeld[i] = 2 then

Delphi-Quellcode:
if SameValue(arGeld[i], 2, 0.005) then
oder
Delphi-Quellcode:
if CompareValue(arGeld[i], 2, 0.005) = EqualsValue then

Sir Rufo 26. Feb 2015 10:13

AW: Genauigkeit von Datentypen
 
Delphi-Quellcode:
var
  Val1, Val2 : Double;

if CompareValue( Val1, Val2 {, Epsilon} ) then
  ShowMessage( 'Sind Gleich' );
Das Epsilon kann ein beliebiger Wert sein, der den maximalen Unterschied angibt, bei dem die Wert noch gleich sind.

Aber warum willst du das Programm überhaupt von
Delphi-Quellcode:
Currency
auf
Delphi-Quellcode:
Double
umstellen? Macht bei Währungsbeträgen doch Null Sinn.

himitsu 26. Feb 2015 10:15

AW: Genauigkeit von Datentypen
 
Und wenn man die 0.005 weg lässt, dann wird automatisch ein typspezifischer Wert genommen.

Delphi-Quellcode:
const
  FuzzFactor = 1000;
  ExtendedResolution = 1E-19 * FuzzFactor;
  DoubleResolution  = 1E-15 * FuzzFactor;
  SingleResolution  = 1E-7 * FuzzFactor;

function SameValue(const A, B: Extended; Epsilon: Extended): Boolean;
begin
  if Epsilon = 0 then
    Epsilon := Max(Min(Abs(A), Abs(B)) * ExtendedResolution, ExtendedResolution);
  ...
end;

function IsZero(const A: Extended; Epsilon: Extended): Boolean;
begin
  if Epsilon = 0 then
    Epsilon := ExtendedResolution;
  ...
end;

PaddyVII 26. Feb 2015 10:40

AW: Genauigkeit von Datentypen
 
Zitat:

Zitat von Sir Rufo (Beitrag 1291590)
[DELPHI]
Aber warum willst du das Programm überhaupt von
Delphi-Quellcode:
Currency
auf
Delphi-Quellcode:
Double
umstellen? Macht bei Währungsbeträgen doch Null Sinn.

ich hatte nie vor es umzustellen, ich hatte nur als erstes double drinstehn, als es nicht funktioniert hat dann single und später erst currency...

mm1256 26. Feb 2015 11:04

AW: Genauigkeit von Datentypen
 
Ob es Sinn macht, statt Double Currency zu verwenden muss wohl jeder für sich selber entscheiden. Es kommt auf den Einzelfall an. Wenn z.B. ein Preis aus Menge mit bis zu 3 Nachkommastellen, Rabatt mit zwei Nachkommastellen, oder sogenannten Multi's wie sie bei Datanorm verwendet werden mit 3 Nachkommastellen....usw. berechnet werden muss, dann muss man Double und Currency mischen. Das ist auch nicht der Hit und darum kann es eleganter sein, die Doubles entsprechend zu behandeln.

Zitat:

Zitat von DeddyH (Beitrag 1291589)
Delphi-Quellcode:
if SameValue(arGeld[i], 2, 0.005) then

... in eine kleine Funktion packen, wenn man Double's für Beträge verwenden möchte

Delphi-Quellcode:
function GleicherBetrag(D1, D2: double): boolean;
begin
  Result := SameValue(D1, D2, 0.011);
end;

himitsu 26. Feb 2015 11:09

AW: Genauigkeit von Datentypen
 
Muss?
Currency = 4 Nachkommastellen

Bei Epsilon=0.011 ist 0.01 = 0.02 .
Epsilon muß kleiner als die Hälfte der kleinsten erlaubten Differenz sein, um "sicher" vergleichen zu können.
Wenn A=3, B=4 und Epsilon=>0.5, dann würde 3.5 verglichen mit A und B jeweils "gleich" sein.

mm1256 26. Feb 2015 12:13

AW: Genauigkeit von Datentypen
 
Zitat:

Zitat von himitsu (Beitrag 1291603)
Muss?
Currency = 4 Nachkommastellen

....oooops....sorry. Verwende Currency nie und hatte was von 2 Nachkommastellen in Erinnerung

Sir Rufo 26. Feb 2015 13:22

AW: Genauigkeit von Datentypen
 
Zitat:

Zitat von mm1256 (Beitrag 1291611)
Zitat:

Zitat von himitsu (Beitrag 1291603)
Muss?
Currency = 4 Nachkommastellen

....oooops....sorry. Verwende Currency nie und hatte was von 2 Nachkommastellen in Erinnerung

Es gibt Länder/Währungen da hat es mehr als 2 Nachkomma-Stellen und die wären ja hübsch angeschmiert. Von daher gab es bei der Definition des Datentyps eine gewisse Weitsicht und man hat dort 4 Stellen vorgesehen. :stupid:

himitsu 26. Feb 2015 13:44

AW: Genauigkeit von Datentypen
 
Andere brauchen mit ihrem Quarter nur eine viertel Nachkommastelle. :stupid:

Und dann noch bissl zum Rechnen, Positionsweise MwSt. usw.

Knapp 9 Billiarden Euro, auf einen tausenstel Euro genau ... da hätte man ruhig noch für ein/zwei Nachkommastellen mehr Platz gehabt. :stupid:

Sir Rufo 26. Feb 2015 14:28

AW: Genauigkeit von Datentypen
 
Zitat:

Zitat von himitsu (Beitrag 1291630)
Andere brauchen mit ihrem Quarter nur eine viertel Nachkommastelle. :stupid:

Und dann noch bissl zum Rechnen, Positionsweise MwSt. usw.

Knapp 9 Billiarden Euro, auf einen tausenstel Euro genau ... da hätte man ruhig noch für ein/zwei Nachkommastellen mehr Platz gehabt. :stupid:

Ich vermute mal, dass die Bedeutung des Datentyps
Delphi-Quellcode:
Currency
etwas verkannt/nicht erkannt wird.

Das ist ein Datentyp für eine Währungs-Buchung. Und ich kenne niemanden, der seine Buchhaltung auf Mikro-Cent führt. Während einer Berechnung eines Buchwertes da nimmt man diese Mikro-Cents mit, aber irgendwann kommt ein Buchwert heraus und der ist dann im EURO-Raum auf 2 Stellen nach dem Komma gerundet (ohne jetzt auf die speziellen Rundung-Arten eingehen zu wollen).

Bei einer Rechnung z.B. erfolgt die Festlegung des Buchwerts auf Positions-Ebene. Jede Position hat also einen festgelegten Buchwert und der ist dann vom Typ
Delphi-Quellcode:
Currency
.

ArtikelPreisMengeBerechneter-WertBuch-Wert
Gold1214,15($/oz.tr.)1(g)1214,15($/oz.tr.) / 31,1034768(g/oz.tr.) * 1(g) = 39,035828946299663($)39,04($)

mensch72 26. Feb 2015 20:47

AW: Genauigkeit von Datentypen
 
Für Anwendungen im Börsenumfeld, wo es durchaus Kurse/Preise auf 8! Nachkommastellen genau gibt, wenn es was mit YEN oder in "1/128" gestuften US Werten zu tun hat...

Wir verwenden dafür einen eigenen 64Bit "FixComma" Datentyp mit 9! Nachkommastellen, welcher Summen und Differenzen absolut rundungsfehlerfrei in Int64 rechnet. Auch speichern wir in dieser Anwendung nur solche Ganzzahl-Int64-Zahlen in Files/Tabellen.

Das Grundprinzip ist simpel: alle Werte sind quasi mit 1'000'000'000 multipliziert und man schafft so alles bis +/- 9'223'372'036','854'775'808 in einem Int64 ganzzahlig unterzubringen.

- Da Delphi ja überladene Operatoren unterstützt, haben wir uns so unseren Eigenen Datentyp auch mit den passenden Funktionen versehen, damit wir ohne Genauigkeitsverlust durch Typumwandlungen auch direkt damit rechnen können und vergleichen können... 11mal -0,11 von 1,21 abgezogen ergibt da wirklich =0 :)
- Per "AsString" als Read/Write Property haben wir auch quasi ein eigenes "FixCommaToStr" bzw. "StrToFixComma" gleich mit eingebaut
- für "Single", "Double" & "Extended" haben wir per eigener impliziter Typumwandlung gleich die passende Epsilon-Rundung eingebaut, um bei "krummen" Eingangswerten die eigentlich gemeinte Zahl zu bestimmen
- für 32Bit Delphi haben wir per Inline-ASM und Coprozessornutzung eine sehr schnelle 128Bit genaue Multiplikation/Division realisiert, weil man da jeweils ein um Faktor 1'000'000'000 falsches Resultat bekommt was sich nur mit 128Bit Ganzzahlgenauigkeit völlig rundungsfehlerfrei berechnen lässt

Der "Zauber" sind einige 100 Quelltextzeilen, aber wenn man sich daran gewöhnt hat mit Kommazahlen ganz normal zu rechnen und zu vergleichen, dann will man es nicht mehr missen.
Egal welche Programmiersprache, ohne eigene selbst geprüfte Routinen/Operationen für Kommazahlen und Datum/Zeit mag ich vor allem im Finanzbereich nicht mehr arbeiten.
Der vorhandene Currency-Type konnte uns nicht überzeugen. Standardlösungen für beliebig "große" Zahlen schieden wegen der Verarbeitungsgeschwindigkeit aus.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:26 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