Delphi-PRAXiS
Seite 2 von 3     12 3   

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)

Andreas13 29. Jan 2021 17:26

AW: FloatToStrF Rundungsfehler ?
 
Zitat:

Zitat von himitsu (Beitrag 1481782)
... 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)

Aber nur, wenn man lediglich addiert oder subtrahiert. Bei sonstigen mathematischen Operationen gibt es auch dort Rundungsfehler.
Andreas

himitsu 29. Jan 2021 19:02

AW: FloatToStrF Rundungsfehler ?
 
In x86 war eine Erweiterung für BCD integriert,
aber im 64 Bit hat es nicht mehr eingebaut und sich den Platz im Silizium gespart.
https://en.wikipedia.org/wiki/Intel_BCD_opcode

Schade, dass es praktisch niemand benutzt hatte und man es somit als Nutzlos ansah. :cry:

egentur 1. Feb 2021 14:48

AW: FloatToStrF Rundungsfehler ?
 
Danke für die vielen Hinweise!

Meine Situation ist folgende:
Ich habe eine Messgerät das den Wert immer mit 5 Nachkommastellen als String liefert. z.B. '0,50015'

Dieser wert wir dann für eine Nebenrechnung mit val() in eine Double Variable konvertiert.


Der User kann dann in der Applikation einstellen, ob er den Wert mit 5 oder weniger Nachkommastellen sehen will.

Mein Problem war eben, wenn er 4 Nachkommastellen einstellt,
wird aus der Double Var mit Floattostrf(var, fffixed,6,4) einmal 0,5002 und einmal 0,5001.

Welche Bedingung bringt Floattostrf dazu an der gleichen Codestelle zwei verschiedene Ergebnisse zu liefern ?

Incocnito 2. Feb 2021 12:01

AW: FloatToStrF Rundungsfehler ?
 
Das Problem hast du mit anderen Zahlen auch bei 2 oder 3 Nachkommastellen.
Ist dir vielleicht nur noch nicht aufgefallen.
Einzig bei 1 Nachkommastelle hättest du das Problem nicht, da es da nur X,5 gibt,
welches in Double (und Float und Extended und whatever) unproblematisch ist.

Du brauchst halt eine generelle Lösung für das Problem und da hilft es dir nur
zu verstehen, warum das passiert. Was du am Ende im Quelltext schreiben musst,
kann dir so vermutlich keiner sagen.

Erst-Bester-Versuch: Addiere ein kleines Diff auf die Zahl drauf.
Wenn du als String immer 5 Nachkommastellen bekommst, dann rechne 1 auf die sechste Nachkommastelle.
z.B. 0,50015 + 0,000001 = 0,500151 ... wenn der Computer
0,500149999999999 davon macht würde er mit 0,50015099999999 arbeiten ...
wenn man den Wert rundet (durch die Anzeige) dann kommt das "richtige" bei rum.
Auch für das Abrunden wäre das unproblematisch, da dir der String ja immer 5 Nachkommastellen liefert.
auch bei 0,50014 wird 0,500141 daraus, welches genauso abgerundet wird wie der "echte" Wert.

[Edit:] Aso, um deine Frage zu beantworten. Alleine durch lustiges Konvertieren oder umspeichern

Delphi-Quellcode:
DerWert := StrToFloatDef(WertAusSensor(), 0); // <- Hier kommt "0,50015"
ShowMessage(FloatToStrF(DerWert, ffFixed, 6, 4));
... ist schon was anderes als ...

Delphi-Quellcode:
ShowMessage(FloatToStr(0.50015, ffFixed, 6, 4));

Liebe Grüße
Incocnito

markus888 2. Feb 2021 20:53

AW: FloatToStrF Rundungsfehler ?
 
Zitat:

Zitat von Incocnito (Beitrag 1481784)
habe ich etwas gebaut, was das kleinste Diff aufaddiert

darf ich fragen, was du da genau machst?

Beim Konvertieren zu einem String, wird ja vermutlich auch eine Differenz aufaddiert.
Hab mich da aber noch nicht tiefer damit beschäftigt.

Incocnito 3. Feb 2021 09:47

AW: FloatToStrF Rundungsfehler ?
 
Zitat:

Zitat von markus888 (Beitrag 1482063)
Zitat:

Zitat von Incocnito (Beitrag 1481784)
habe ich etwas gebaut, was das kleinste Diff aufaddiert

darf ich fragen, was du da genau machst?...

Ich ermittel je nach Datentyp die kleinste Differenz, welche beim Aufaddieren den Wert der Variable noch ändert.
Zum Verständnis: Wenn du 4 Bits als Nachkommastellen hättest, könntest der Computer ja
1,00 / 1,0625 / 1,1250 / 1,1875 / 1,2500 / 1,3125 / 1,3750 / 1,4375 / 1,5000 / 1,5625 / 1,6250 / ...
abbilden.
Die Genauigkeit welche ich brauche darf dann natürlich nur entsprechend sein. In dem Fall mit 4 "Nachkomma-Bits"
reicht es nichmal für 1 Nachkommastelle, aber ich kann hier nicht eine Liste für 10 Nachkomma-Bits machen,
damit wir 2 Nachkommastellen zum "Spielen" haben.
Wenn ich '1,4' als Wert bekomme sagt der PC "Ok, 1,3750 ist näher dran als 1,4375, dann speicher ich '1,4' als 1,3750".
Ich rechne in diesem Fall stumpf 0,0625 auf alle ermittelten Ergebnisse.
Wenn ich also den Wert 1,3750 sehe wird daraus 1,4375. Welcher bei der Ausgabe wieder unkritisch wäre.

Ja, wie gesagt, die 4 Bits reichen leider nicht für 1 Nachkommastelle, wie man bei der 1,5000 sieht.
Ich denke mal, dass man 7 Nachkomma-Bits bräuchte (1/128 = 0,0078125) für dieses
Prinzip.
Rechne ich bei 1,5000 die 0,0625 drauf würde gerundet ja tatsächlich 1,6 heraus kommen.
Wenn ich
http://docwiki.embarcadero.com/RADSt...ormate_(Delphi)
richtig lese hat selbst Single 23 Nachkomma-Bits.
Für eine Anwendung die 4 Nachkommastellen braucht ist das also ausreichend.

Anmerkung für die Allwissenden unter euch: Nein, ich lasse die Verschiebung der Mantisse
jetzt mal absichtlich weg und beziehe mich nur auf die Differenz zum Wert 1,0. ;-)
Sonst wird das echt zu konpliziert.


Zitat:

Zitat von markus888 (Beitrag 1482063)
...Beim Konvertieren zu einem String, wird ja vermutlich auch eine Differenz aufaddiert.
Hab mich da aber noch nicht tiefer damit beschäftigt.

Der Computer bahandelt hier nur die Darstellung als Text. Das ist nur
numerisch gesehen ein Aufaddieren (oder Abziehen), lass dich davon nicht beirren.

---

Hast du mal versucht auf die Werte, welche du vom Sensor bekommst einfach etwas
drauf zu rechnen? ... Du könntest zum Testen sogar hingehen und sowas machen:
Delphi-Quellcode:
var
  dWert : Single; // oder Double
  sWert : String;
...
  sWert := WertAusSensor();
  dWert := StrToFloatDef(sWert, 0);
  if (dWert <> Trunc(dWert)) then sWert := sWert + '1'; // Nur wenn Nachkommastellen, sonst machst du ja aus "2" eine "21" ;-)
  dWert := StrToFloatDef(sWert, 0);
  ShowMessage(FloatToStrF(dWert, ffFixed, 6, 4));
Ob man jetzt stiltechnisch sagt "ich mache StrToFloat() und fange die Exception ab/nicht ab" oder
ob man für den Default-Wert einen unrealistischen Wert nimmt und eine Meldung ausgibt,
falls dieser Wert dann heraus kommt oder man direkt mit "TryStrToFloat" arbeitet,
kann sich meinetwegen jeder selbst aussuchen.
Für deinen speziellen Fall dürfte das so aber reichen.

Anmerkung: Da es je nach Anwendungsfall anders gehandhabt werden muss, können Programmiersprachen
das nicht "von sich aus korrigieren". Delphi bietet allerdings mit dem Datentyp "Currency" eine
gute Möglichkeit solche Fehler mit Hausmitteln einfach zu bekämpfen, wenn man 4 Nachkommastellen
und weniger braucht. Leider reicht das für deinen Fall hier nicht.

Melde dich, ob du mit meinem Ansatz das Problem beheben konntest.

LG Incocnito

Andreas13 3. Feb 2021 23:31

AW: FloatToStrF Rundungsfehler ?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von egentur (Beitrag 1481932)
... Meine Situation ist folgende: Ich habe eine Messgerät das den Wert immer mit 5 Nachkommastellen als String liefert. z.B. '0,50015'
Dieser wert wir dann für eine Nebenrechnung mit val() in eine Double Variable konvertiert.
Der User kann dann in der Applikation einstellen, ob er den Wert mit 5 oder weniger Nachkommastellen sehen will.
Mein Problem war eben, wenn er 4 Nachkommastellen einstellt,
wird aus der Double Var mit Floattostrf(var, fffixed,6,4) einmal 0,5002 und einmal 0,5001.
Welche Bedingung bringt Floattostrf dazu an der gleichen Codestelle zwei verschiedene Ergebnisse zu liefern ?

Hallo Egentur,
ich kann Dein ursprüngliches Problem nicht reproduzieren: Bei mir (XE5 Pro) funktioniert FloatToStrF (..) korrekt. Vielleicht ist noch ein Bug in XE2? Daher habe ich für Dich einige Routinen für die manuelle Rundung als Ersatz von FloatToStrF(..) im beiliegenden Testprogramm zusammengestellt.
Gruß, Andreas

bcvs 4. Feb 2021 08:05

AW: FloatToStrF Rundungsfehler ?
 
Das Problem war ja nicht FloatToStrF, sondern die Tatsache, dass 0.50015 in einem double zu 0.50014999999 wird und dann auf 4 Stellen gerundet 0.5001. Die Funktion, mit der gerundet wird, spielt dabei keine Rolle,

Ändere mal in deinem Beispiel den Typ von "Zahl" zu double. Dann sieht man das schön.

egentur 4. Feb 2021 12:09

AW: FloatToStrF Rundungsfehler ?
 
Hallo bvcs

Mein Problem war schon floattostrf

Selbst wenn ich den Wert der Double Variablen explizit auf 0.500149999999999983 setze ( was ja dann bei floattostrf(var,fffixed,6,4) 0.5001 ergeben müsste

habe ich bei den Aufrufen immer das Ergebnis 0.5002

Erst nach einem Ausdruck egibt es dann 0.5001 und bleibt auch so, bis zu einem erneuten Prgogrammstart.

Welche Randbedingungen ( Luftdruck, Zimmertemperatur ) führen denn dazu, das floattostrf bei zwei identischen ( an gleicher Stelle) Aufrufen
eine unterschiedliches Ergebnis liefert ?

Moombas 4. Feb 2021 12:38

AW: FloatToStrF Rundungsfehler ?
 
Kann das Problem also im Quickreport sein und nicht in der Funktion?
Darüber schon mal nachgedacht/geforscht?


Alle Zeitangaben in WEZ +2. Es ist jetzt 06:54 Uhr.
Seite 2 von 3     12 3   

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