Delphi-PRAXiS
Seite 1 von 3  1 23   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   FloatToStrF Rundungsfehler ? (https://www.delphipraxis.net/206772-floattostrf-rundungsfehler.html)

egentur 28. Jan 2021 09:09

FloatToStrF Rundungsfehler ?
 
Hallo zusammen

Habe hier eine wahrscheinlich simple Frage.

In einer Software (Delphi 6) habe ich ein Problem mit der Funktion

Var
RV : Double;
Result :String;
begin
Result := FloatToStrF(RV,ffFixed,6,4);
end;

RV ist 0.50015
Beim ersten Aufruf ist Result korrekt 0.5002

Das bleibt auch so, bis ich den Wert mal mit QuickReport ausdrucke.

Danach ergibt der gleiche Aufruf der Funktion

RV immer noch 0.50015
Result = 0.5001

Wenn RV vom Typ Single ist, dann bleibt Result korrekt 0.5002

Hat hier FloatToStrF ein Problem mit dem übergebenen ParameterTyp ?

Danke für jeden Hinweis !

egentur 28. Jan 2021 12:51

AW: FloatToStrF Rundungsfehler ?
 
Nachtrag

Im Debugger sind die Parameter für die Funktion FloatToStrF korrekt.

Ich habe nur den Eindruck das die Funktion einmal rundet und dann (nur nach einem Ausdruck) einfach ein truncate macht!

Der schöne Günther 28. Jan 2021 13:23

AW: FloatToStrF Rundungsfehler ?
 
Ist das wieder der Mythos mit den Druckertreibern welche die FPU-Einstellungen verdrehen?
Macht es einen Unterschied wenn man es an einen PDF-Drucker sendet?

egentur 28. Jan 2021 13:36

AW: FloatToStrF Rundungsfehler ?
 
Hallo

Leider macht es keinen Unterschied welchen Druck man benutzt.
Im Prwview vom Fastreport kann ich auch nach PDF exportieren ( kein Problem ),
nur wenn ich drucke reagiert die Funktion nachher anders.

Das löst sich nur auf , wenn ich die Applikation ganz schließe !

Rollo62 28. Jan 2021 17:01

AW: FloatToStrF Rundungsfehler ?
 
Ich bin ja nicht 100% sicher, aber FloatToStrF würde ich nicht fürs Runden missbrauchen.
Insbesondere wenn es mir wirklich auf die letzte Stelle ankommt.
Das steuert doch nur die Anzeige, und die Rundungsfunktion ist Abfallprodukt.
Evtl. vorher Runden, wie gewünscht mit der gewünschten Rundungs-Funktion, und dann erst FloatToStrF.

Incocnito 28. Jan 2021 17:29

AW: FloatToStrF Rundungsfehler ?
 
Ja scheint auf den ersten Blick seltsam ...

0.5025 beispielsweise wird bei IEEE-754 (das müsste Single sein)
als
0.502499997615814208984375
gespeichert (dichter kommt der PC mit der Speicherung in 32-Bit nicht ran)
Dort würde ein Runden auf 3 Nachkommastellen nicht wie erwartet
0.503 sondern 0.502 ergeben.

Runden war schon immer ein Spaß für sich.

Dein 0.5015 wäre in IEEE-754 (Single?) übrigens 0.50150001049041748046875.
Es hilft also nur bei genau dieser Zahl von Double auf Single zu wechseln,
bei anderen Zahlen schlägt es genau umgekehrt fehl, bei noch anderen Zahlen
wäre der Datentyp dann vermutlich egal.

Wenn du 4 Nachkommastellen als Maximum hast könnte Currency gehen,
oder du baust deine eigene "sichere" Runden-Funktion.

MfG Incocnito

egentur 29. Jan 2021 16:37

AW: FloatToStrF Rundungsfehler ?
 
Hallo Incocnito

Genauso verhält es sich bei mir.
Ich habe die 8 Jahre alte Software übernommen.

Ein Messwert kommt als String '0.50015' ,
wird dann für eine weitere Berechnung in Double konvertiert

val(messwert, double, error) double ist dabei 0.500149999...

und dann wieder in String
result := Floattostrf(double , fffixed, 6,4)

result ist dann '0,5001'

zur Anzeige.

Warum rundet die Funktion an diese Stelle ab statt auf (0,5002) ?

Ist dies ein bekannter Fehler ?

Delphi.Narium 29. Jan 2021 16:48

AW: FloatToStrF Rundungsfehler ?
 
Weil 0.500149999 bei Rundung auf vier Nachkommastellen halt 0.5001 ergibt.

Floattostrf weiß nicht, dass vorher aus dem String 0.50015 ein Double 0.500149999 gemacht wurde und daraus dann eigentlich ein String 0.50015 werden müsste, der dann zu 'nem gerundeten String 0.5002 werden sollte.

Floattostrf kennt nunmal nur den aus dem String erstellten Doublewert von 0.500149999, rundet den auf vier Nachkommastellen = 0.5001 und macht dann daraus einen String.

himitsu 29. Jan 2021 16:57

AW: FloatToStrF Rundungsfehler ?
 
Warum?
Das wurde doch gesagt und du hast es selbst auch nochmal bestätigt.

Intern wird es als 0.500149999... gespeichert (Darstellungsfehler, da wir in einem 10er-System denken, aber der Computer mit einem 2er-System rechnen muss.
(10 lässt sich nunmal nicht als 1 Bit darstellen und wird somit zwangsläufig unscharf, denn 8-10-16)

Und bei Rundung auf 4 Nachkommastellen, kommt für 0.500149999 nunmal 0.5001 raus.


Wenn man davon ausgehen könnte, dass bei String-zu-Float immer abgerundet würde,
dann könnte man vor dem Runden einen kleinen Offset zurechnen (z.B. 0.00001 also 10% von der kleinstmöglichen Nachkommastelle) und schon stimmt es.
Aber ich vermute fasst hier wird das Nächstliegendste gespeichert.
Würde dabei manchmal aufgerundet, also 0.500150000001..., dann müsste man den Offset ja eigentlich abziehen.



Aus diesem Grund gibt es z.B. auch den Typ Currency (ist intern ein Int64, wo die letzten 4 Dezimalstellen als Nachkomma definiert sind, womit es dort nicht solche Rundungsfehler gibt, bei bis zu 4 Nachkommastellen)
Man kann auch BCD oder andere Big-Nummer-Systeme verwenden, wenn es sein muß. (BCD speichert in einem 10er-System und hat somit keinen Darstellngsfehler)

Incocnito 29. Jan 2021 17:09

AW: FloatToStrF Rundungsfehler ?
 
Ok, Himitsu war schneller ... ich poste das aber trotzdem jetzt! ;-)

1) Um Gedanklich ungefähr dahin zu kommen zu verstehen, was da passiert:
Man nehme an, für die Nachkommastellen gäbe es im Computer nur 2 Bits.
Durch die Beschaffenheit eines Computers könnte dieser nur auf 1/4-Werte speichern.
Er kann also nur 0.000, 0.250, 0.500 und 0.750 (und 1.000 usw.) speichern.
Jetzt kommst du mit dem Wert "0.100" und der Computer muss sich entscheiden,
ob er das als 0.000 oder als 0.250 speichern soll.
Der Computer hat herausgefunden, dass 0.000 dichter am Originalwert ist, also
speichert er den Wert als diesen ab.
Wenn du dann fragst "Wert <> 0" sagt der Computer "Nein", obwohl dein Ursprungswert
sehr wohl <> 0 wäre.

Vielleciht hilft auch das hier:
https://www.matheretter.de/rechner/dezimalbinar

2) Der Datentyp Currency hilft hier, da er in Wirklichkeit den 10000-stel Wert
speichert. (4 Nachkommastellen)
Speicherst du 1,20 € als Currency speichert er 12000 als Integer (Int64), weiß aber
wenn du damit arbeiten willst, dass du nur den 10000sten Teil meinst.
Für das Runden von Beträgen, welche in Float-Werten (Real, Float, Double, Extended)
in irgendeiner Form zwischengespeichert wurden habe ich etwas gebaut,
was das kleinste Diff aufaddiert, die Kommastellen (um 2 Stellen) verschiebt
0,5 aufaddiert, dann das ganze Truncated und den Wert wieder zurück verschiebt.
Hierbei klappte das Runden dann bisher immer auch bei Werten aus Fließkommavariablen.
Andere Funktionen hatten immer irgendeine Zahl, bei der das Runden
dann doch nicht funktionierte. Eben wegen der Beschaffenheit der Fließkommazahlen
in der EDV.

Das war schon wieder viel zu viel Text. Sorry.

Liebe Grüße
Incocnito


Alle Zeitangaben in WEZ +2. Es ist jetzt 06:45 Uhr.
Seite 1 von 3  1 23   

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