Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - String to Single


digiveit - Mi 22.10.08 10:39
Titel: String to Single
Hallo zusammen,

ich gebe eine Kommazahl in ein Edit-Feld ein. Diese soll dann in eine Variable vom Typ Single umgewandelt werden und mit libnodave an eine S7 geschickt werden.
Jetzt habe ich das Problem, dass nach der Zuweisung an die Single-Variable mir Nachkommastellen erzeugt werden, die ich nicht haben möchte.
Hat jemand eine Ahnung warum das so ist?

Hier mein Quellcode:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
  s1: WideString;
  e1: Double;
  sgl1: Single;

begin

  s1 := edtGewicht.Text;  // Eingabe: 1,369
  e1 := StrToFloat(s1);   // e1 = 1,369
  sgl1 := e1;             // sgl1 = 1,368999958 <- WARUM IST DAS SO?!?!? 
  NoDave.WriteFloat(12, e1);
end;


Hab es auch schon mit RoundTo usw. probiert, aber ohne Erfolg.
Was mache ich da falsch?

Vielen Dank schon mal für Eure Hilfe!

MfG
Veit

Moderiert von user profile iconmatze: Code- durch Delphi-Tags ersetzt


hazard999 - Mi 22.10.08 10:47

Auszug aus WP:

[zitat]
Schon einfache Dezimalzahlen, z. B. 0,1, können nicht mehr exakt als binäre Gleitkommazahlen dargestellt werden, da viele im Dezimalsystem abbrechende Kommazahlen im Binärsystem nicht abbrechende, periodische Zahlen sind; von diesen werden nur die ersten p Ziffern gespeichert, wodurch Ungenauigkeit entsteht. Dezimal 0,1 ist binär 0,0001100110011… In einem binären Gleitkommasystem ist also 10 · 0,1 < 1, da die 0,1 abgerundet wird und nicht den exakten Wert approximiert.
[/zitat]

Das passiert wenn du einen Datentyp grösserer Genauigkeit in einen mit kleiner Genauigkeit umwandelst.


Gausi - Mi 22.10.08 10:49

Das liegt an der internen Darstellung der Zahl im Binärsystem. Der Wert 1,369 kann im Dualsystem nicht genau dargestellt werden (oder nur mit sehr viel mehr Stellen), so dass es dann zu Ungenauigkeiten kommt.

Wirklich beheben kann man das nicht, evtl. ist der Datentyp Currency was für dich. Oder klappt vielleicht die direkte Zuweisung MySingleVar := StrToFloat(sMyString); besser?


hazard999 - Mi 22.10.08 10:49

Einmal konvertieren ist besser:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var s1: string;
    sing: single;
begin
   s1 := Edit1.Text;
   sing := StrToFloatDef(s1, 0);
end;


Timosch - Mi 22.10.08 11:11

Warum überhaupt Single? Warum nicht Extended?


digiveit - Mi 22.10.08 11:34

user profile iconTimosch hat folgendes geschrieben Zum zitierten Posting springen:
Warum überhaupt Single? Warum nicht Extended?

Weil ich mit libnodave die Daten an eine SPS S7-Steuerung schicken möchte und dort der Übergabeparameter ein Single ist:

Delphi-Quelltext
1:
NoDave.WriteFloat(12, sgl1);                    

Ich möchte halt in der SPS genau 1,369 drin stehen haben und nicht 1,368999958.
Muss doch irgendwie machbar sein, oder nicht?!?

MfG
Veit


mkinzler - Mi 22.10.08 12:24

Dann runde den Wert auf die gewüsnchte Anzhal von Stellen


digiveit - Mi 22.10.08 15:43

user profile iconmkinzler hat folgendes geschrieben Zum zitierten Posting springen:
Dann runde den Wert auf die gewüsnchte Anzhal von Stellen

Hab ich auch schon probiert.
Geht aber nicht

Delphi-Quelltext
1:
sgl2 := RoundTo(sgl1, -3);                    

liefert mir in sgl2 wieder den Wert mit den vielen Kommastellen.

Noch andere Vorschläge?

MfG
Veit


Boldar - Mi 22.10.08 15:46

bei drei Nachkommastellen nimm doch currency!


alzaimar - Mi 22.10.08 16:27

Lass man, eigentlich ist das das Gleiche (1,369 vs. 1,368999958), Du bemängelst einen Fehler von 0,000003%, das ist lächerlich. Es ist nunmal so, wie oben schon erwähnt, das sich die Zahl 1,369 *NICHT* exakt in einem Single-Datentyp darstellen lässt. Punkt.

Grundsätzlich: Bei Floating Point-Datentypen muss man die letzten 2-3 Stellen ignorieren.

Bei Deinem Problem würde ich (nun nicht gerade bei dem einfachen Beispiel) mit Double oder Extended rechnen und dann den Wert vor der Übergabe in einen Single umrechnen. Mit Single würde ich nur sehr einfache Rechnungen durchführen.

Wenn Du z.B. einen Stab der Länge 100cm hast, ist der garantierte nie genau 100cm lang. In diesem Fall wäre er 100,000003 cm lang. Na nun.


Boldar - Do 23.10.08 14:10

Aber wenn man currency nimmt, treten KEINE Rundungsfehler auf!!
Weil Currency-Daten nicht als Gleitkommazahlen gespeichert werden, sondern als integer.


alzaimar - Do 23.10.08 14:18

user profile iconBoldar hat folgendes geschrieben Zum zitierten Posting springen:
Aber wenn man currency nimmt, treten KEINE Rundungsfehler auf!!

Und wenn man sie in Single konvertiert? :lol: Die tritt doch spätestens bei der Übergabe an die SPS auf.

Currency ist ein sehr schöner Datentyp, wenn man nur addiert bzw. subtrahiert, weil man sich dann nicht mit den Schutzstellen herum ärgern muss. Als allgemeiner Datentyp in wissentschaftlich-mathematischen Anwendungen eignet er sich jedoch aufgrund der beschränkten Genauigkeit nicht.


Boldar - Do 23.10.08 14:36

currency-->nach string
string---> nach single
konvertieren, dann treten keine Rundungsfehler auf.


alzaimar - Do 23.10.08 14:44

user profile iconBoldar hat folgendes geschrieben Zum zitierten Posting springen:
konvertieren, dann treten keine Rundungsfehler auf.

Doch, weil 1,369 kein Element der Menge der im Datentyp 'Single' darstellbaren Zahlen ist. Das wurde hier doch nun schon mehrfach erklärt.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
Procedure TForm1.ZeigsMir;
Const
  Zahl = '1,369';

Var
  s: Single;
  d : Double;
  e : Extended;

begin
  s := StrToFloat(Zahl);
  d := StrToFloat(Zahl);
  e := StrToFloat(Zahl);
  memo1.Lines.Add(Format('Single  : %8.15f',[s]));
  memo1.Lines.Add(Format('Double  : %8.15f',[d]));
  memo1.Lines.Add(Format('Extended: %8.15f',[e]));
end;

Wie schaffst Du es, das in einem Single die Zahl '1,369' steht?


Boldar - Do 23.10.08 18:10

ok, garnicht...
Du hast recht.
Denkfehler^^