Autor Beitrag
Phil24696
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Mo 19.08.13 15:32 
Hi,

da ich nicht genau wusste in welche Kategorie ich mein Problem/ meine Frage schreiben soll, habe ich einfach mal die Kategorie Sonstiges gewählt.
Also mein Problem betrifft Delphi7.
Während der Erstellung eines Programmes(Konsolenanwendung) mit dem man einen "Realwert", also eine Dezimalzahl, in die ganze Zahl und den Rest teilt.

Das wäre das Programm:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
program test1;
{$APPTYPE CONSOLE}
uses
  sysutils;

var
x,y,erg : real;

begin
write('Geben Sie eine Dezimalzahl ein: ');
readln(x);
writeln;

erg:=trunc(x);
y:=x-erg;

writeln('Die Dezimalzahl mit Komma betraegt:  ',x:0:30);
writeln('Die Dezimalzahl ohne Komma betraegt: ',erg:0:30);
writeln('Der Wert hinter dem Komma betraegt:  ',y:0:30);

readln;
readln;
end.


Das Programm an sich funktioniert, aber bei der Ausgabe ist mir aufgefallen, dass die eingegebenen Werte verfälscht werden. (angefügt ist ein Screenshot während das Programm ausgeführt wird)

Meine Frage daher ist, warum werden die Eingaben verfälscht.
Liegt es an meinem Programm, wenn ja was ist daran falsch oder ist dies ein Delphiproblem?
Einloggen, um Attachments anzusehen!
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 19.08.13 15:51 
Real ist bei Dir Double. Double haben eine Rundungsfehler-Einheit von 2.2E-16, also hat es keine Sinn soviele Nachkommastellen ohne Spezialroutinen auszugeben; jedenfalls nicht für solche Zahlen, die nicht exakt darstellbar sind. Für 2.25 statt 2.45 sollte es klappen, der Realwert für 2.45 ist
ausblenden Quelltext
1:
2.45000000000000017763568394002504646778106689453125.					

Für diesen Beitrag haben gedankt: Phil24696
Phil24696 Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Di 20.08.13 00:42 
Danke für die Antwort. :D
Aber ich habe doch die Variable als Realwert deklariert und nicht als "Double". (Ich kenne mich leider noch nicht so gut in dieser Materie aus, wäre also sehr nett wenn du mir eine Erklärung, insbesondere zum Unterschied zwischen den beiden, geben könntest)
Wie kann ich dies dann so eingegeben oder welche Deklarierung muss ich vornehmen, dass meine Nachkommastellen nicht verfälscht werden?

Zitat:
also hat es keine Sinn soviele Nachkommastellen ohne Spezialroutinen auszugeben...

Was ist hier gemeint mit "Spezialroutinen"?

Ich wollte eigentlich ein Programm erstellen, dass berechnet wie viel Nachkommastellen eine Zahl hat. Hat auch funktioniert, doch leider wird ja der Wert scheinbar verfälscht, daher ist das Programm soweit unbrauchbar.

Ich würde mich auf eine Lösung meines Problemes sehr freuen und natürlich auch auf eine ausführliche Erklärung warum das Programm einen anderen Wert in Real ausgibt, als ich eingebe.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 20.08.13 08:39 
user profile iconPhil24696 hat folgendes geschrieben Zum zitierten Posting springen:
Aber ich habe doch die Variable als Realwert deklariert und nicht als "Double".
Real heißt "Nimm irgendeinen Typ mit Nachkommastellen", das ist eigentlich kein konkreter Typ. Mit Single oder Double gibst du selbst die Genauigkeit an.

Für diesen Beitrag haben gedankt: Phil24696
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Di 20.08.13 09:23 
user profile iconPhil24696 hat folgendes geschrieben Zum zitierten Posting springen:
Ich wollte eigentlich ein Programm erstellen, dass berechnet wie viel Nachkommastellen eine Zahl hat. Hat auch funktioniert, doch leider wird ja der Wert scheinbar verfälscht, daher ist das Programm soweit unbrauchbar.

Ich würde mich auf eine Lösung meines Problemes sehr freuen und natürlich auch auf eine ausführliche Erklärung warum das Programm einen anderen Wert in Real ausgibt, als ich eingebe.
Ich hole mal ein wenig weiter aus: Normalerweise rechnen Computer bei Fließkommazahlen in der Basis 2 statt 10, also die binäre 0.1 ist 2^-1 = 1/2. Wieviel Dezimalstellen hat 1/3 oder Wurzel aus 2? Klar, unendlich viele. Damit kann man aber auch dezimal nicht mechanisch rechnen, also nimmt man zB 10 Nachkommastellen und einen Exponenten: 1/3 = 0.3333333333*10^0. Was ist 3*1/3? Exakt mit Brüchen 3*1/3 = 1, mit unendlichen vielen Stellen 0.99999999999999999..., aber mit 10 Stellen 0.9999999999 und das ist ungleich 1!

Zur Binäerproblemetik: Es gibt (für uns) wichtige Zahlen, die nicht exakt im 2-er-System darstellbar sind, vor allem
ausblenden Quelltext
1:
2:
1/10(dez) = 0.000110011001100110011001100110011001100110011001100110011..(bin)
 1/3(dez) = 0.010101010101010101010101010101010101010101010101010101010..(bin)
Exakt als Double bzw Real darstellbar sind im Prinzip alle Zahlen der Form 53-Bit-Integer * Zweierpotenz also zB 2.25 = 225/100 = 9/4 = 9*2^(-2) aber nicht 2.45 = 245/100 = 49/20.

Daß nicht alle Real-Zahlen darstellbar sind, ist nun allerdings kein Grund zur Verzweiflung. Die 16-Stellen bei Double reichen meistens völlig aus. Nur ist es wie gesagt sinnlos, mehr als 16 Nachkommastellen anzeigen zu wollen! Im Gegenteil: es ist eher sinnvoll weniger auszugeben, da dann meist (aber nicht in allen Fällen!) richtig gerundet wird, und Fantasie-Stellen verschwinden.
user profile iconPhil24696 hat folgendes geschrieben Zum zitierten Posting springen:
Was ist hier gemeint mit "Spezialroutinen"?
Das sind zum Beispiel Routinen, die auf der Form Integer*Zweierpotenz aufbauen und diese durch Umrechnen ins Dezimalsystem mit langer Multiplikation und Division Stelle für Stelle erzeugen, bzw. allgemeine (Pascal-)Bibliotheken wie mein MPArith, mit der die oben gezeigten Beispiel gerechnet wurden.


Zuletzt bearbeitet von Gammatester am Di 20.08.13 10:03, insgesamt 2-mal bearbeitet

Für diesen Beitrag haben gedankt: Martok, Phil24696
hathor
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 20.08.13 10:55 
Wenn's um Geld geht: Currency.
Dafür ist es da.
Man kann es natürlich auch für andere Berechnungen nehmen...
www.delphibasics.co....TL.asp?Name=Currency

Für diesen Beitrag haben gedankt: Phil24696
Phil24696 Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Di 20.08.13 22:49 
Danke für die informativen Antworten!
Also ich schließe aus den erhaltenen Informationen folgendes:
Damit mein Programm funktioniert soll ich die Deklarierung "Currency" verwenden oder zumindestens einen genauere Deklarierung als Real.
@ Gammatester: Also wenn ich dich richtig verstehe wird eine Zahl, z.B. 2,45, die ich eingebe und sofort wieder ausgeben lasse, verfälscht, da ein Computer eine inkorrekte Basis verwendet, oder?

Zitat:
53-Bit-Integer
Was ist damit gemeint?
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mi 21.08.13 00:00 
user profile iconPhil24696 hat folgendes geschrieben Zum zitierten Posting springen:
Damit mein Programm funktioniert soll ich die Deklarierung "Currency" verwenden
Currency hat genau nichts mit der Frage zu tun. Das ist ein 4-Stellen Fixkomma-Typ (1.2345 exakt), der bringt dich nicht weiter. Den verwendet man dann, wenn man diese Floating-Point-Eingenschaften um jeden Preis vermeiden muss, nämlich dann, wenn's um Geld geht. Es gibt prinzipiell keine Rundungsfehler, dafür aber eben auch nur 4 Stellen. Solange man nicht dividiert (und das tun Buchhalter selten), ist das kein Problem.

user profile iconPhil24696 hat folgendes geschrieben Zum zitierten Posting springen:
oder zumindestens einen genauere Deklarierung als Real.
Im Prinzip ja, aber auch bei Extended (das wäre der {mit Einschränkungen} genaueste Typ den Delphi bietet) hast du das gleiche Problem, nur ein paar Stellen später.

user profile iconPhil24696 hat folgendes geschrieben Zum zitierten Posting springen:
@ Gammatester: Also wenn ich dich richtig verstehe wird eine Zahl, z.B. 2,45, die ich eingebe und sofort wieder ausgeben lasse, verfälscht, da ein Computer eine inkorrekte Basis verwendet, oder?
Falsch ist da garnichts. Das ist halt eine Eigenschaft von Fließkommaarithmetik, dass nur bestimmte Zahlen exakt darstellbar sind. Alle anderen können nur angenähert werden.

user profile iconPhil24696 hat folgendes geschrieben Zum zitierten Posting springen:
Zitat:
53-Bit-Integer
Was ist damit gemeint?
Eine Ganzzahl mit 53bit und Vorzeichen, also im Bereich von -2^52 bis 2^52-1 (-4 503 599 627 370 496 ... 4 503 599 627 370 495)

Mal ein Beispiel (bitte korrigiert mich, ich lern das auch grade erst :lol:):
1.0 könnte man einfach ausdrücken als
ausblenden Quelltext
1:
  1                * 2^0     = 1.00000000000000					

Die nächstkleinere darstellbare Zahl ist dann
ausblenden Quelltext
1:
  4503599627370495 * 2^(-52) = 0.999999999999999778					

was nämlich exakt
ausblenden Quelltext
1:
2:
3:
  4503599627370495
  ---------------- = 0.999999999999999777955395074968691915273666381835938
  4503599627370496

entspricht. Eigentlich wirst du sogar weniger als die 18 Stellen sehen, da dieser Effekt ja bekannt ist und daher FloatToStr() freiwillig nur so viele Stellen ausgibt, wie theoretisch abbildbar sind. Die konkrete Zahl kann sogar weniger Stellen haben, in diesen Fällen sieht man dann den Unterschied zwischen Eingabe und Ausgabe.

Zahlen zwischen dieser "fast-1" und 1 sind schlicht nicht darstellbar, werden im gewählten Datentyp also auf eine der beide Stellen gerundet. Wenn du jetzt nach der Anzahl der Stellen in
ausblenden Quelltext
1:
                               0.999999999999999800					

fragst, bekommst du (je nach Rundungs-Einstellungen) entweder die vielen Stellen der "fast-1" oder "keine".

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."

Für diesen Beitrag haben gedankt: Phil24696
Oliver Marx
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 80
Erhaltene Danke: 18

Win 7 Prof.
Delphi XE Prof.
BeitragVerfasst: Mi 21.08.13 00:02 
Hi,
was meinst du mit inkorrekter Basis?
Eine 32-bit Variable kann unabhängig vom genauen Typ genau 4294967296 verschiedene Werte annehmen. Da aber bei einer Singlevariablen der Wertebereich von -1,5x10^-45 bis 3,4x10^38 geht, kann single nicht alle möglichen Werte in diesem Intervall annehmen (dazu kommen bei single auch noch nicht-zahl Werte). Daher wird für eine Konstante der Singlewert genommen, der der Konstanten am nächsten ist.

Für diesen Beitrag haben gedankt: Martok, Phil24696
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mi 21.08.13 09:41 
user profile iconOliver Marx hat folgendes geschrieben Zum zitierten Posting springen:
Hi,
was meinst du mit inkorrekter Basis?
Eine 32-bit Variable kann unabhängig vom genauen Typ genau 4294967296 verschiedene Werte annehmen. Da aber bei einer Singlevariablen der Wertebereich von -1,5x10^-45 bis 3,4x10^38 geht, kann single nicht alle möglichen Werte in diesem Intervall annehmen (dazu kommen bei single auch noch nicht-zahl Werte). Daher wird für eine Konstante der Singlewert genommen, der der Konstanten am nächsten ist.
Ich glaube, hier verwechselst Du ein paar Sachen (korrigier mich, wenn ich falsch liege):

Wenn user profile iconPhil24696 von "inkorrekter Basis" spricht, meint er wohl, daß ihm Basis-2 für Fließkomma falsch oder unangemessen erscheint. Für viele Anwendungen (zB bei Finanzen/Buchhaltung) hat er damit vollkommen recht! Es gibt sogar im neuen IEEE-Standard die Basis-10 Fließkommazahlen und einige Software/Hardware-Implementation unterstützen das auch. In der Regel aus Perfermanzgründen und historisch ist allerdings die binäre Darstellung (noch) am meisten verbreitet. Und für die meisten Delphi-Versionen ist die Frage akademisch; es gibt allerdings für neuere Versionen zB von Rudy Velthuis eine Dezimal-Unit (von mir nicht getestet, kann also nicht viel mehr dazu sagen).

32-Bit-Integer und Singlevariablen haben mit dem Thema hier nichts zu tun, da wir Double-Variable betrachten, die nun mal eine 53-Bit Mantisse (52 echte Bits plus 1 Bit implizit) haben, den Rest hat user profile iconMartok schon geklärt.

Für diesen Beitrag haben gedankt: Phil24696
Oliver Marx
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 80
Erhaltene Danke: 18

Win 7 Prof.
Delphi XE Prof.
BeitragVerfasst: Mi 21.08.13 11:25 
Hi,
ich denke, dass meine Aussage schon etwas damit zu hat, da er real, bzw. double als Typ gewählt hatte und sich über die Ungenauigkeit gewundert hat. 2 ist aber die Basis für alle nativen (hardwareunterstützten) Gleitkommazahlen und genau aus diesem Grund tritt sein Problem auf. Und ob er nun single oder double wählt, macht für den Grund des Fehlers keinen Unterschied, nur der absolute Fehler ist bei double geringer. Weitere, detailliertere Informationen zu nativen Gleitkommazahlen gibt es in der entsprechenden Definition (IEEE 754).
Ich habe meinen Post auch geschrieben, da ich öfters von Freunden ähnliche Probleme kenne, wie z.B. auch Vergleich mit "=" bei Gleitkommazahlen im Allgemeinen, da diese einen größeren Zahlenbereich als Integer haben möchten und daher zu single wechseln, da single die gleiche Bitbreite aber einen größeren Zahlenraum abdeckt. Viele erkennen allerdings nicht, dass die Anzahl an exakt darstellbaren Zahlen nur von der Bitbreite abhängt und sonst nichts.
Ich stimme dir daher auch vollkommen zu, dass er keinen nativen Gleitkommatyp sondern z.B. mit Currency arbeiten sollte oder eine nicht-nativen Gleitkommatyp, der mit der Basis 10 arbeitet.

Für diesen Beitrag haben gedankt: Phil24696
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19312
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 21.08.13 17:51 
user profile iconOliver Marx hat folgendes geschrieben Zum zitierten Posting springen:
2 ist aber die Basis für alle nativen (hardwareunterstützten) Gleitkommazahlen
Außer bei CPUs wie IBMs Power6.

Für diesen Beitrag haben gedankt: Phil24696
Oliver Marx
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 80
Erhaltene Danke: 18

Win 7 Prof.
Delphi XE Prof.
BeitragVerfasst: Mi 21.08.13 19:20 
Ich war jetzt nur von Plattformen ausgegangen, die von Delphi unterstützt werden. Natürlich gibt es auch Architekturen, die andere Normen für Datenrepräsentationen nutzen.
Phil24696 Threadstarter
Hält's aus hier
Beiträge: 11



BeitragVerfasst: Mi 21.08.13 20:15 
Erneut vielen Dank für die informativen Beiträge, die mittlerweile eine eigenständige Diskussion entwickelt haben. :D

Durch den sehr informativen Beitrag von user profile iconMartok ist mir nun klar das ich mein Problem auf meine alte Weise wohl kaum lösen könnte, da nicht alle Zahlen von theoretisch "-unendlich" bis "+unendlich" darstellbar sind (die Problematik ist ja hierbei scheinbar die vom Computer ausgehende Verwendung der Basis 2 zur Annährung an eine nächste Zahl)

An user profile iconOliver Marx:
Also was ich mit inkorrekter Basis meine ist liegt klar auf der Hand und wurde hier auch schon erkannt:
Die Basis 2, die der Computer verwendet, ist ja inkorrekt in der Hinsicht die Stellen exakt darzustellen und keine Annährungs- oder Rundungsproblematiken auftreten zu lassen. Somit für mein Programm eine inkorrekte Basis (eingegebener Wert ungleich ausgegebener/dargestellter Wert) user profile iconGammatester hat das genauer erläutert was mein Problem damit ist.

Zitat:
eine nicht-nativen Gleitkommatyp, der mit der Basis 10 arbeitet.

Wie könnte ich so einen Typ in meinem Programm verwenden? (zusätzliche Frage: Was bedeutet "nicht-nativ"?)

Ungeachtet meines Problems habe ich eine andere Lösung ohne Verwendung von currency (was auch eine meiner Lösungsvarianten ist, jedoch das Rundungsproblem ab der 4 Stelle besitzt, also mehr als 4 Stellen berechnet das Programm daher nicht.) gefunden.
Hier wäre der Quellcode zu dieser Lösungsvariante:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
program DeCa;
{$APPTYPE CONSOLE}
uses
  sysutils;

(* errechnet zu einer gegebenen Zahl die Dezimalstellen aus.*)

var
  zeichen       : char;
  zaehler, dezimalstellen : integer;

begin

  writeln ('Geben sie eine Dezimalzahl ein.');

  dezimalstellen := 0;

  Repeat
    read(zeichen);

    Case zeichen of

     '1''2''3''4''5''6''7''8''9''0'  :  zaehler := 1;
     '.'','                      :  zaehler := 0;

    End(* of case *)
    if zaehler=0 then
    dezimalstellen :=dezimalstellen*0
    else
    dezimalstellen := dezimalstellen + zaehler;

  Until eoln;

  writeln;
  writeln('Dezimalstellen:  ', dezimalstellen);

  readln;
  readln;

end.