Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - FloatToStrF und "geltende Ziffern"


Spaceguide - Di 25.07.06 12:59
Titel: FloatToStrF und "geltende Ziffern"
Wie krieg ich FloatToStrF dazu, sich wie folgend zu verhalten: Es sollen maximal vier geltende Ziffern angezeigt werden, keine Darstellung mit Exponent (also ffFixed) und unnötige Nullen hinten nicht angehängt werden. Beispiele:

Single => String

0.001234 => 0.001 (round)
0.0001234 => 0
1.0e-5 => 0
99.994 => 99.99
99.996 => 100

etc.


n-regen - Di 25.07.06 13:36

"Delphi für Kids" von Hans-Georg Schumann:
Zitat:
FloatToStrF(Zahl, Format, Genauigkeit, Kommastellen)
[...]
FloatToStrF(Input, ffNumber, 8, 2)
Hilft dir das weiter?
Übrigens:
Genauigkeit bringt nicht wirklich was.


Spaceguide - Di 25.07.06 13:45

Nein, das hilft kein bisschen weiter.


digi_c - Di 25.07.06 14:35

Der gute alte Format('%f',[zahl]) Befehl hilft da bestimmt. Genaueres hab ich aber bei der Hitze auhc nciht mehr im Kopf...


Spaceguide - Di 25.07.06 14:47

Auch Format erlaubt mir nicht die gewünschte Ausgabe.


Lannes - Di 25.07.06 15:47

Hallo,

passt das ?

Delphi-Quelltext
1:
Format('%.4g',[StrToFloat(Format('%.3f',[e]))]);                    


Spaceguide - Di 25.07.06 15:51

Nein, 10000 wird zu 1E004


Lannes - Di 25.07.06 15:55

user profile iconSpaceguide hat folgendes geschrieben:
Nein, 10000 wird zu 1E004

und was soll bei 12345 herrauskommen?


Spaceguide - Di 25.07.06 15:57

Es soll etwas wie diese Funktion, die ich mir gestrickt habe, machen, nur ein bisschen eleganter.


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:
type float = single;

function MyFloatToStr(const aFloat : float; aDigits : byte = 7): string;
{
 Converts the single precision variable aFloat to a string with
 aDigits-significant numbers

 Example : PointFloatToStr(1000.4,4) = '1000'
           PointFloatToStr(0.0001,4) = '0'
           PointFloatToStr(-123.46,4) = '-123.5'

}

 var dspos,left,right : integer;
     precision : integer;
begin
 precision := Max(Trunc(Ceil(ln((Abs(aFloat)+1))/ln(10))),aDigits);

 Result := FloatToStrF(aFloat,ffFixed,precision,10);
 dspos := Pos(DecimalSeparator,Result); //position of decimal separator
 if dspos>0 then
 begin
  left := Pos('-',Result)+1;

  if dspos-left<aDigits then
   right := left+aDigits
  else
   right := dspos;

  while Result[right]='0' do Dec(right); //remove trailing zeroes
  if Result[right]=DecimalSeparator then Dec(right); //remove unnecessary separator

  Result := Copy(Result,1,right);
 end;
end;


Spaceguide - Di 25.07.06 15:59

user profile iconLannes hat folgendes geschrieben:
user profile iconSpaceguide hat folgendes geschrieben:
Nein, 10000 wird zu 1E004

und was soll bei 12345 herrauskommen?


Da soll dann natürlich 12345 rauskommen, genauso wie man es z.B. im Physikunterricht gemacht hat.


JayEff - Di 25.07.06 16:04

Kannst doch versuchen, hinterher das E mit *10^ zu ersetzen und das dann aus zu rechnen? also du suchst dir die stelle mit dem E und machst dann vielleicht

Delphi-Quelltext
1:
2:
3:
4:
ZahlHinterE := StrToInt(copy(result,pos('E', result), length(result)));
Result:=StringReplace(result, 'E'+IntToStr(ZahlHinterE), '', []);
for i:=1 to zahlHinterE do
   result:=result+0;

Kann aber sein, dass ich das problem nicht ganz verstanden hab ^^


Lannes - Di 25.07.06 23:23

Hallo,

12345 bei 4 signifikanten Stellen = 12345 :gruebel:

kannst ja mal das antesteten:
:!: unit math einbinden


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TForm1.Button3Click(Sender: TObject);
var aDigits : byte;
    e : extended;
begin
  aDigits := StrToInt(Edit2.Text);
  e := StrTofloat(Edit1.Text);
  if Round(Log10(ABS(e)))+1 > aDigits then
    Label1.Caption := IntToStr(round(e))//round oder trunc  ????
    else
      Label1.Caption := Format('%.'+IntToStr(aDigits)+'g',
                               [StrToFloat(Format('%.'+IntToStr(aDigits-1)+'f',[e]))]
                               );


Spaceguide - Mi 26.07.06 08:08

user profile iconLannes hat folgendes geschrieben:

12345 bei 4 signifikanten Stellen = 12345 :gruebel:


Nunja, wenn ich mich recht erinnere, war das im Physikunterricht so. Man sollte ja die Endergebnisse mit einer sinnvollen Stellenzahl angeben. Vor dem Komma wurde aber nix weggeschnippelt.


Sy- - Mi 26.07.06 09:55

Genügt nicht einfach folgendes:

Delphi-Quelltext
1:
2:
3:
Zahl:=123456.78634;
Fa:=100//das wären 2 Stellen hinterm Komma
result:=round(Zahl*Fa)/Fa;

da wird auch nichts abgekürzt oä.

Hier mal als Function wie du es brauchst:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
function myRound(Zahl:String; Stellen:integer=2):string;
var
  fa,x:integer;
  gZahl:real;
begin
  fa:=1;
  for x := 0 to fa do fa:=fa*10;
  
  try
    gZahl:=strtofloat(Zahl);
    result:=floattostr(round(gZahl*fa)/fa);
  except
    result:='0';
  end;
end;


Gruß


Spaceguide - Mi 26.07.06 10:27

Testet doch einfach mal eure Funktionen gegen die oben genannten Anforderungen, bevor ihr sie postet.


digi_c - Mi 26.07.06 11:31

o.k. aber wie sind denn die genauen Bedingungen?

Zitat:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function MyFloatToStr(const aFloat : float; aDigits : byte = 7): string;  
{  
 Converts the single precision variable aFloat to a string with  
 aDigits-significant numbers  
 
 Example : PointFloatToStr(1000.4,4) = '1000'  
           PointFloatToStr(0.0001,4) = '0'  
           PointFloatToStr(-123.46,4) = '-123.5'  
 
}

Reicht mir nicht so richtig, wann muss Vorzeichen beachtet werden usw...


0.001234 => 0.001 (round) Also alle Nachkommas auf 3 Stellen?
0.0001234 => 0 Sobald 4 Stellen keine Kommas=0?
1.0e-5 => 0 Verdammt kleine Zahlen auf =0
99.994 => 99.99 Bei positiven Zahlen auch 2 Nachkommastellen?
99.996 => 100 Bei allen Zahlen aufrunden?


crowley - Mi 26.07.06 11:40

die funktion ist getestet und in mehreren anwendungen im einsatz... ich habe das mit deinen "testzahlen" von oben auch getestet... und es funktioniert...

ABER schau mal selbst bei dir oben: du hast in deinen Musterlösungen einen unterschiedliche Anzahl von erlaubten Nachkommastellen

hier: 0.001234 => 0.001 (round)
hier: 99.994 => 99.99

dementsprechend musst du das natürlich im Offset-Parameter anpassen.
Desweiteren wird hier richtig gerundet und nicht im Delphi- typischen
Banker's Rounding (bei einem exakten Nachkomma- Anteil von .5 wird immer zur "gerade" Zahl gerundet).

Um die Funktion nutzen zu können, musst du die Unit Math einbinden


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
function RoundEx(const X: Extended; const Offset: integer): Extended;
begin
  if frac(X * Power(10, OffSet)) >= 0.5 then
       result := ceil(X * Power(10, OffSet)) / Power(10, OffSet)
  else result := floor(X * Power(10, OffSet)) / Power(10, OffSet);
end;


um das dann wie von dir gewünscht einsetzen zu können, kannst du dann bespielsweise

Delphi-Quelltext
1:
  Label1.Caption := FloatToStr(RoundEx(StrToFloat(edit1.Text), 3));                    


aufrufen.

C.

Anmerkung noch am Rande: Patzig den Leuten gegenüber sein, die dir helfen wollen, ist nicht die feine Art.


Sy- - Mi 26.07.06 11:49

Spaceguide, jetzt wird es zwar OT aber:

Arbeite mit den Scripten die du bekommst und verlang nicht auf dich exakt geschneiderte Scripte.
Wir sind hier um dir zu helfen, nicht um deine Arbeit zu erledigen.

Meins funktioniert nämlich auch und wie crowley schon sagte muss ein parameter mehr rein.

Fällt mir schwer zu glauben, dass du den anderen Script selber gecoded hast wenn du nichtmal unsere Scripte anwenden kannst.

Gruß


JayEff - Mi 26.07.06 16:07

Jaja.. Sy- wird wieder ausfallend, offensiv und spricht von oben herab, nur um zu verdecken, dass er das Problem nicht verstanden hat.
Hier gehts darum, eine Zahl nicht auf ihre Nachkommastellen, sondern auf ihre stellen Insgesammt zu runden, oder? Also während 1,12344 zu 1,234(4?) wird, wird 12,344 zu 12,34(4?), oder? Versuch doch noch ein paar mehr Beispiele zu geben, und wenn du im endeffekt nur 4 ziffern haben willst, was wird dann aus 12345? die zahl verändert sich ja maßgeblich, wenn du einfach eine stelle weglässt, weil du keine 5 sondern 4 ziffern haben willst.


jasocul - Mi 26.07.06 16:49

Probiere mal das:

Delphi-Quelltext
1:
2:
3:
4:
5:
function GerundeteZahl(Zahl : Extended; Rundung : Integer) : Extended;
begin
  while trunc(zahl) > power(10, rundung) do inc(rundung);
  result := RoundTo(zahl, Rundung);
end;

Du benötigst die Unit Math.
Ich habs nur mit zwei Werten getestet. Als Rundungswert musst du in deinen Beispielen -3 vorgeben.
Für weitere Infos kannst du auch in die Delphi-Hilfe schauen.


Sy- - Mi 26.07.06 16:50

Ich sehe schon ich bin dir Sympatisch JayEff :wink:

Aber du hast recht, etwas in der Richtung meint er wo ich drüber sah.
Vor dem Komma sollte scheinbar nichts weggetan werden.
Da frage ich mich aber wo der Sinn überhaupt sein soll in der Funktion :roll:


Spaceguide - Mi 26.07.06 20:37

user profile iconSy- hat folgendes geschrieben:
Spaceguide, jetzt wird es zwar OT aber:
Arbeite mit den Scripten die du bekommst und verlang nicht auf dich exakt geschneiderte Scripte.
Wir sind hier um dir zu helfen, nicht um deine Arbeit zu erledigen.


Also wenn ich irgendwas haben will, dann kann ich auch gleich FloatToStrF nehmen, das funktioniert noch besser als das was du fabriziert hast. Wenn einer fragt, wie man zwei Zahlen addiert und du schreibst a-b und meckerst danach, dass er es sich gefälligst selbst anpassen soll, dann bist du überhaupt keine Hilfe.

Zitat:

Meins funktioniert nämlich auch und wie crowley schon sagte muss ein parameter mehr rein.


myRound('1234.123123',4) = 1234.12 => FALSCH


Zitat:

Fällt mir schwer zu glauben, dass du den anderen Script selber gecoded hast wenn du nichtmal unsere Scripte anwenden kannst.


Diplom-Informatiker, 10 Jahre Delphi-Erfahrung ... ich kann programmieren, glaub's mir.


JayEff - Do 27.07.06 00:15

[OT]
user profile iconSy- hat folgendes geschrieben:
Ich sehe schon ich bin dir Sympatisch JayEff :wink:

Woran liegt das nur, dass ich Sarkasmus erkenne... (ich verweise an dieser Stelle an den Zusatz meiner Signatur, nicht dass du mich schon wieder auf Rechtschreibfehler aufmerksam machst: 18 jähriger Gymnasiast, Mathe 15 Punkte, tut mir ja leid, dass ich in Deutsch weniger gut bin.)

user profile iconSy- hat folgendes geschrieben:
Da frage ich mich aber wo der Sinn überhaupt sein soll in der Funktion :roll:

Ich weis nicht, wie oft du dich mit den Gedanken anderer MEnschen auseinander gesetzt hast bzw deren Fragen zu Funktionen beantwortet hast: Die Frage stellt sich häufig. Ich habe allerdings in eigener Erfahrung bemerkt, dass Menschen IMMER einen Grund für ihr handeln haben und wenn wir nicht jede Zeile seines Programms kennen, was unwahrscheinlich ist, können wir nicht wissen, ob seine Funktion wirklich Sinn macht. Aus diesem Grund sollten wir so gut als möglich seine Frage beantworten und nicht seine Programmierkenntnisse auf überhebliche Art in Frage stellen. Versteh mich nicht falsch. Du hast dich schon 2 mal über andere Leute lustig gemacht, und ich glaube nicht, dass du selbst der Meinung bist, das würde in irgenteiner Art helfen... Also lass es doch bitte ;>
Zu deiner Signatur: Vollkommen richtig, gefällt mir :D , aber.. fällt das nicht unter den Kündigungsschutz? :motz: [/OT]


Zurück zum Thema.
user profile iconjayeff hat folgendes geschrieben:
Hier gehts darum, eine Zahl nicht auf ihre Nachkommastellen, sondern auf ihre Stellen insgesamt zu runden, oder? Also während 1,12344 zu 1,234(4?) wird, wird 12,344 zu 12,34(4?), oder? Versuch doch noch ein paar mehr Beispiele zu geben, und wenn du im endeffekt nur 4 ziffern haben willst, was wird dann aus 12345?

Ähm ich muss gestehen, dass ich mir deinen QT nicht angeguckt hab...


digi_c - Do 27.07.06 08:48

user profile iconSpaceguide also dein Ton ist wirklich ein bischen komisch wenn man bedenkt, dass wir hier zusammensitzen um dir bei deinem Problem zu helfen...


jasocul - Do 27.07.06 09:02

Leute, haltet doch mal den Ball flach. So führt das doch zu nichts.
Wenn einer mal einen Parameter vergessen hat, das kann mal passieren. Bei meinem Beispiel habe ich auch erst On-The-Fly programmiert und hinterher eine Funktion daraus gemacht. Dabei hatte ich auch was vergessen (ist übrigens korrigiert). Ja und? Ist doch menschlich. Da muss man niemanden einen Vorwurf machen und der Fehler-Produzent muss nicht sauer sein, wenn das bemerkt wird.


Sy- - Do 27.07.06 09:09

Spaceguide, wenn das mit FloatToStrF so ginge, dann würdest du es auch damit machen. Hast doch vorhin sogar selbst jede Variante verneint, weil da immer diese Abkürzungen kommen.
Das deine Reaktion unfreundlich war ist nicht zu bestreiten.

Du hast das Problem, dass du dich sehr schlecht ausdrückst. Obwohl ich jetzt schon gepostet habe, dass ich es nicht verstehe und JayEff eine Frage zusätzlich stellt ist es für mich fragwürdig wie man dir helfen soll.
Ich habe gemeint, dass ich es richtig verstanden habe und war deswegen etwas biestig. Das war darauf bezogen, dass die Variablen namen und die funktion gleich heißen sollte und das habe ich nicht eingesehen.

Nun ist klar, dass es anders sein soll.
Aber wie?
Die Frage ist ja nicht wie das Ergebnis auszusehen hat, sondern warum es so auzszusehen hat.
Du hättest nun ruhig sagen können:
myRound('1234.123123',4) = 1234.12 => FALSCH
myRound('1234.123123',4) = 1234 => RICHTIG
myRound('234.123123',4) = 234.1 => RICHTIG

Schon hätten wir dir helfen können, da es unsere Frage beantwortet :roll:

Aber nun für den Sinn. Also so sehe ich den Sinn, hat auch gewisse Vorteile.
Für meinen Geschmack aber fehlt dann für höhere Zahlen eine Abkürzung wie K (Tausend) und M usw. dass man kurze Zahlen hat und halt das relevanteste auch bei nicht so niedrigen. Vielleicht hat man auch ein Beschränktes Display.

und folgendes:
Zitat:

Diplom-Informatiker, 10 Jahre Delphi-Erfahrung ... ich kann programmieren, glaub's mir.

Ich möchte ja niemanden lustig machen, auch wenn es sich manchmal so anhört, aber der Diplom und 10Jahre Erfahrung reichen dir nicht für eine saubere Lösung dieser Aufgabe? Ich würde das wie mit einer Kanone auf einen Spatz sehen aber dem ist nicht so.

Bevor wir hier aber noch länger Diskutieren, habe ich mir einfach dein Script angeschaut und hier ist meine saubere Lösung dazu:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Function myRound(Zahl:real; Gen:integer):String;
var
  i,c:integer;
begin
  i:=ansipos(',',pchar(floattostr(zahl)));
  c:=gen-i+1;
  if(c<0)then c:=0;
  result:=floattostr(round(zahl*power(10,c))/power(10,c));
end;


Grüße


Spaceguide - Do 27.07.06 09:40

user profile iconSy- hat folgendes geschrieben:

Ich möchte ja niemanden lustig machen, auch wenn es sich manchmal so anhört, aber der Diplom und 10Jahre Erfahrung reichen dir nicht für eine saubere Lösung dieser Aufgabe? Ich würde das wie mit einer Kanone auf einen Spatz sehen aber dem ist nicht so.


Ich habe es ja gelöst gehabt, aber mir sah das zu pfuschig aus, weshalb ich mal nach zweiten Meinungen gefragt habe. Es ist besser eine Standardfunktion zu verwenden, als selbst etwas zu stricken.


Zitat:

Bevor wir hier aber noch länger Diskutieren, habe ich mir einfach dein Script angeschaut und hier ist meine saubere Lösung dazu:


myRound(0.00001,4) = 1E-5 => FALSCH (sollte 0 sein)
myRound(-100.123,4) = -100 => FALSCH (sollte -100.1 sein)
Komma ist hard-kodiert


Sy- - Do 27.07.06 10:21

Das mit dem Minus hab ich vergessen.
Wegen dem 1E-05 hab ich einfach mit dem Single gelöst, hat halt den Nachteil, dass er höhere Zahlen und ganz kleine Zahlen nicht mehr erfasst. Da es aber bei dir im Einsatz war, denke ich, dass es zu keinen Problemen führen sollte.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Function myRound(Zahl:single; Gen:integer):String;
var
  i,c,m:integer;
begin
  if(zahl<0)then m:=-1 else m:=1;
  zahl:=zahl*m;
  i:=ansipos(',',pchar(floattostr(zahl)));
  c:=gen-i+1;
  if(c<0)then c:=0;
  result:=floattostr(round(zahl*power(10,c))/power(10,c)*m);
end;


jasocul - Do 27.07.06 11:05

Meine gestrige Variante war wohl ziemlich doof. Hier eine neue, die mit allen hier angegebenen Zahlen das richtige Ergebnis bringt:

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:
function GerundeteZahl(Zahl : Extended; Rundung : Integer) : String;
var
  i : Integer;
begin
  if trunc(abs(zahl)) = 0 then
    dec(rundung);
  if trunc(abs(zahl)) >= power(10, rundung) then
  begin
    result := FloatToStr(trunc(zahl));
  end
  else
  begin
    i := 0;
    while trunc(abs(zahl)) > 0 do
    begin
      inc (i);
      zahl := zahl / 10;
    end;

    zahl := zahl * power(10, rundung);
    zahl := round(zahl);
    zahl := zahl / power(10, rundung-i);
    result := FloatToStr(zahl);
  end;
end;


Lannes - Do 27.07.06 17:14

Hallo,

@user profile iconSpaceguide, hast Du eigentlich meinen zuletzt geposteten Code getestet?
Zum Beitrag in diesem Thread [http://www.delphi-forum.de/viewtopic.php?p=376356#376356]
Setzt man dort round ein, IMHO entspricht das Ergebnis Deinem und dem zuletzt von user profile iconSy- geposteten Code.
Mit trunc entspricht er IMHO user profile iconjasoculs letztem Code.

Hab den Code mal in eine Funktion verpackt und übersichtlicher(besser?) gecodet:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function FloatToStrDigits(e: extended;aDigits: byte): String;
begin
  if Round(Log10(ABS(e)))+1 > aDigits then
    Result := IntToStr(round(e))//trunc, dann 12345,6 = 12345
    else
      begin
      Result := Format('%.*f',[aDigits-1,e]);
      Result := Format('%.*g',[aDigits,StrToFloat(Result)]);
      end;
end;{function FloatToStrDigits(...)}


Spaceguide - Do 27.07.06 17:29

@Lannes: Schmiert bei 0 ab.


JayEff - Do 27.07.06 17:31

Wenn sichs nur um 0 dreht, machste halt ein if e=0 then Result:='0' else begin {alte funktion} end; rein ^^