AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

SimpleRoundTo

Ein Thema von MrSpock · begonnen am 24. Dez 2012 · letzter Beitrag vom 29. Dez 2012
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von MrSpock
MrSpock
(Co-Admin)

Registriert seit: 7. Jun 2002
Ort: Owingen
5.865 Beiträge
 
Delphi 2010 Professional
 
#1

SimpleRoundTo

  Alt 24. Dez 2012, 22:13
Ich habe schon die Fragen dazu im Forum gelesen, aber habe immer noch keine gute Antwort gefunden.

Ich benutze SimpleRoundTo, da ich kein kaufmännisches Runden haben möchte.

Habe zwei Extended Werte a = 0.55 und b = 18.5. Das Produkt ist 10.175 müsste also mit SimpleRountTo(a*b, -2) aufgerundet werden auf 10.18.

Im Debugger wird mir der Wert von
Zitat:
(AValue / LFactor) - 0.5) mit 1018
abgezeigt, der Wert von
Zitat:
Trunc(AValue / LFactor) - 0.5) mit 1017
!!??? Grrml

Jetzt habe ich in der Math Bibliothek mal Trunc durch Round ersetzt und mit mrRoundnearest gerundet. Dann zeigt der Degugger:
Zitat:
(AValue / LFactor) - 0.5) mit 1018
uns auch
Zitat:
Round(AValue / LFactor) - 0.5) mit 1018
, aber
Zitat:
(AValue / LFactor) - 0.5)*LFactor mit 10,17
.

Ich will doch einfach nur einen "normal" gerundeten Wert haben.

Wie erhalte ich hier den aufgerundeten Wert 10.18?
Albert
Live long and prosper


MrSpock
  Mit Zitat antworten Zitat
Benutzerbild von Bummi
Bummi

Registriert seit: 15. Jun 2010
Ort: Augsburg Bayern Süddeutschland
3.470 Beiträge
 
Delphi XE3 Enterprise
 
#2

AW: SimpleRoundTo

  Alt 24. Dez 2012, 22:50
ich habe kein Problem mit:
Delphi-Quellcode:
var
 a,b,c:extended;
begin
a :=0.55;
b := 18.5;
c:=RoundTo(a*b,-2);
Caption := FloatToStr(c);
end;
aber vielleicht habe ich etwas falsch verstanden.
Thomas Wassermann H₂♂
Das Problem steckt meistens zwischen den Ohren
DRY DRY KISS
H₂ (wenn bei meinen Snipplets nichts anderes angegeben ist Lizenz: WTFPL)
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#3

AW: SimpleRoundTo

  Alt 25. Dez 2012, 19:49
Ich habe vor etlichen Jahren einen fiesen Bug in einer Abrechnungssoftware gehabt, aus ähnlichen Gründen.

Ich haben dann die Konstante 0.5 auf 0.5+1E-15 (also ein winzigkleinwenigmehr als 0.5) geändert. (Ich glaube zumindest, das es 1E-15 war, keine Ahnung)

Ich will doch einfach nur einen "normal" gerundeten Wert haben.
Na ja, so (aber das weißt Du ja):
Trunc(unroundedValue/Granularity+0.5+1E-15)*Granularity
  Mit Zitat antworten Zitat
Volker Z.

Registriert seit: 3. Dez 2012
Ort: Augsburg, Bayern, Süddeutschland
419 Beiträge
 
Delphi XE4 Ultimate
 
#4

AW: SimpleRoundTo

  Alt 26. Dez 2012, 01:19
Hallo,

ich kann Dein Problem mit dem Rundungfehler leider - noch nicht ganz - nachvollziehen. In Anlehnung an Bummis Beispiel ergibt sich bei mir für:

Delphi-Quellcode:
var
  a, b : Extended;
begin
  a := 0.55;
  b := 18.5;

  Caption := FloatToStr(SimpleRoundTo (a * b))
end;
stets der Wert 10,18 (auch mit anderen Werten für a und b die entsprechend korrekte Rundung); also das, was Du eigentlich auch erwartest.

Gehe ich hingegen her und kopiere mir sozusagen den Code aus SimpleRoundTo und nehme:

Delphi-Quellcode:
var
  f : Double;
  a, b, c : Extended;
begin
  a := 0.55;
  b := 18.5;

  f := IntPower (10, -2);
  c := Trunc((a * b / f) + 0.5) * f;

  Caption := FloatToStr(c)
end;
erhalte ich das fehlerhafte Ergebnis 10,17. Für a, b, c : Double; erhalte ich korrekte Ergebnisse (SimpleRoundTo erwartet an der Stelle auch Double). Versuch' s doch mal mit Double für a und b.

Ich haben dann die Konstante 0.5 auf 0.5+1E-15 (also ein winzigkleinwenigmehr als 0.5) geändert. (Ich glaube zumindest, das es 1E-15 war, keine Ahnung)
Ich will jetzt ja nicht klugsch..., aber nur ein "winzigkleinwenigmehr" in Form von 1E-15 aufzuaddieren scheint mir dann doch nicht die geeignete Lösung zu sein. Besser wäre dann doch mit der relativen Maschinengenauigkeit zu rechnen, als einen x-beliebigen Wert zu nehmen.

Gruß
Volker Zeller
  Mit Zitat antworten Zitat
Popov
(Gast)

n/a Beiträge
 
#5

AW: SimpleRoundTo

  Alt 26. Dez 2012, 06:09
Das sollten klappen. Ob es schön ist, ist eine andere Frage.

Delphi-Quellcode:
function MyRound(Value: Extended): Extended;
begin
  //bei 100 wird auf 2 Stellen gerundet, bei 1000 auf 3, usw.
  Result := Trunc(Value) + (Round(Frac(Value) * 100) / 100);
end;
Macht aus 12,34567 > 12,35

Macht aus 12,34467 > 12,34
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#6

AW: SimpleRoundTo

  Alt 26. Dez 2012, 09:47
Popov, bei deiner Lösung wird das Problem auch irgendwann auftreten.
Ich will jetzt ja nicht klugsch...,
Dann lass es doch einfach.

Geändert von Furtbichler (26. Dez 2012 um 10:08 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von MrSpock
MrSpock
(Co-Admin)

Registriert seit: 7. Jun 2002
Ort: Owingen
5.865 Beiträge
 
Delphi 2010 Professional
 
#7

AW: SimpleRoundTo

  Alt 26. Dez 2012, 13:45
So wie Furtbichler habe ich es in der Vergangenheit auch gemacht.

Ich habe das Testprogramm von Bummi auch getestet und da funktioniert es.

Die Daten kommen aber aus einer Firebird DB und sind TFibFloatFields. Aber ich habe alle Multiplikationen durch Int64 Multipilkationen ersetzt.

Etwa so:

Delphi-Quellcode:
Function Mult3(e1, e2, e3: Extended): Extended;
...
i1 := Round(SimpleRoundTo(e1*1000), 0);
i2 := Round(SimpleRoundTo(e2*1000), 0);
...
Result := i1*i2*i3/1000000000;
end;
Damit berechne ich dann
Delphi-Quellcode:
Erg := Mult3(feld1, feld2, 1.0); // feld1 ist ein DB Feld mit Float Wert 0.55 und Feld2 mit 18.5;

Erg := SimpleRoundTo(erg, -2);
Und dann wird 10,17 ausgegeben. Warum ist das unterschiedlich zu dem Code von Bummi?
Albert
Live long and prosper


MrSpock

Geändert von MrSpock (26. Dez 2012 um 13:51 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von MrSpock
MrSpock
(Co-Admin)

Registriert seit: 7. Jun 2002
Ort: Owingen
5.865 Beiträge
 
Delphi 2010 Professional
 
#8

AW: SimpleRoundTo

  Alt 26. Dez 2012, 13:50
Hallo,

ich kann Dein Problem mit dem Rundungfehler leider - noch nicht ganz - nachvollziehen. In Anlehnung an Bummis Beispiel ergibt sich bei mir für:

Delphi-Quellcode:
var
  a, b : Extended;
begin
  a := 0.55;
  b := 18.5;

  Caption := FloatToStr(SimpleRoundTo (a * b))
end;
stets der Wert 10,18 (auch mit anderen Werten für a und b die entsprechend korrekte Rundung); also das, was Du eigentlich auch erwartest.

Gehe ich hingegen her und kopiere mir sozusagen den Code aus SimpleRoundTo und nehme:

Delphi-Quellcode:
var
  f : Double;
  a, b, c : Extended;
begin
  a := 0.55;
  b := 18.5;

  f := IntPower (10, -2);
  c := Trunc((a * b / f) + 0.5) * f;

  Caption := FloatToStr(c)
end;
erhalte ich das fehlerhafte Ergebnis 10,17. Für a, b, c : Double; erhalte ich korrekte Ergebnisse (SimpleRoundTo erwartet an der Stelle auch Double). Versuch' s doch mal mit Double für a und b.

Ich haben dann die Konstante 0.5 auf 0.5+1E-15 (also ein winzigkleinwenigmehr als 0.5) geändert. (Ich glaube zumindest, das es 1E-15 war, keine Ahnung)
Ich will jetzt ja nicht klugsch..., aber nur ein "winzigkleinwenigmehr" in Form von 1E-15 aufzuaddieren scheint mir dann doch nicht die geeignete Lösung zu sein. Besser wäre dann doch mit der relativen Maschinengenauigkeit zu rechnen, als einen x-beliebigen Wert zu nehmen.

Gruß
Also das mit dem Double werde ich versuchen! Hast du eine Begründung, warum Extended hier nicht funktioniert? Das hat doch eine bessere Auflösung.

Ich glaube ich versuche einmal die Rundungsfunktionen hier http://cc.embarcadero.com/Download.aspx?id=21909
Albert
Live long and prosper


MrSpock

Geändert von MrSpock (26. Dez 2012 um 14:17 Uhr)
  Mit Zitat antworten Zitat
samso

Registriert seit: 29. Mär 2009
439 Beiträge
 
#9

AW: SimpleRoundTo

  Alt 26. Dez 2012, 16:12
Die Floatingpointunit des Prozessors rechnet intern mit dem Typ Extended. Wenn die Zwischenergebnisse im Typ Double gespeichert werden, dann wird auf die geringere Auflösung gerundet (vergleichbar mit einem Taschenrechner der intern mit 12 Stellen rechnet in der Anzeige aber nur 10 Stellen darstellt). Solange ich innerhalb eines Typs bleibe (also Double oder Extended) ist das Endergebnis bei mir korrekt. Erst bei der Mischung der Typen kommt es zu sichtbaren Fehlern. Wenn also a,b vom Typ Extended sind, während f vom Type Double ist, ist der Fehler der Berechnung bei mir so groß, dass das Ergebnis der Rundung falsch ist.
Und dann wird 10,17 ausgegeben. Warum ist das unterschiedlich zu dem Code von Bummi?
Das wäre nur zu erklären, wenn das Ergebnis der Funktion Mult3 nicht 10,175 sondern kleiner wäre. Da der Fehler schon sehr groß sein muss, damit er im Debugger angezeigt wird, käme es vermeintlich zur falschen Rundung. Mit dem folgende Testprogramm habe ich das getestet:
Delphi-Quellcode:
var
  a: Extended;
begin
   a := 10.175 - 5E-10;
   Caption := FloatToStr(SimpleRoundTo(a));
Der Debugger (Delphi 2007/2010) zeigt a=10,175 an, obwohl der Fehler bereits bei 5E-10 liegt, und damit weit oberhalb der eigentlich erreichbaren Genauigkeit von Extended. Mir ist allerdings nicht klar, wie das Ergebnis von Mult3 in diesen Bereich kommen sollte. Ich habe mich redlich bemüht mit diesen Anfangswerten zu diesem Fehlerbild zu kommen - es klappt nicht.
Edit: Die Berechnung (a * b / f) + 0.5 ergibt einen Wert der etwas kleiner als 1018 ist (der Fehler sollte in etwa bei -2e-14 liegen). Bei der Zuweisung auf Double wird nun durch die Floatingpointunit auf 1018 gerundet. Das nachfolgende Trunc liefert dann den korrekten Integer 1018. Unterbleibt die Zuweisung auf Double und wird mit Extended weiter gerechnet, unterbleibt die prozessorinterne Rundung und trunc liefert den "falschen" Wert.

Bei Delphi 2007 war die Funktion SimpleRoundTo wie folgt deklariert:
function SimpleRoundTo(const AValue: Double; const ADigit: TRoundToRange = -2): Double;

Bei Delphi 2009/2010 (spätere Versionen habe ich hier nicht) war es dann
function SimpleRoundTo(const AValue: Extended; const ADigit: TRoundToRange = -2): Extended;

Deshalb liefert die Funktion bei Delphi 2007 noch den korrekten Wert und bei Delphi 2009/2010 dann nicht mehr.

Geändert von samso (27. Dez 2012 um 12:07 Uhr) Grund: Inhaltliche Fehler beseitigt
  Mit Zitat antworten Zitat
Benutzerbild von MrSpock
MrSpock
(Co-Admin)

Registriert seit: 7. Jun 2002
Ort: Owingen
5.865 Beiträge
 
Delphi 2010 Professional
 
#10

AW: SimpleRoundTo

  Alt 28. Dez 2012, 15:27
Volker Z. hat oben gepostet, wie er es nachbilden konnte.

Obwohl ich auch nicht verstehe warum der Effekt bei der Berechnung auftritt, habe ich (höchstwahrscheinlich) jetzt eine Funktion gefunden, die korrekt funktioniert. Es ist die Funktion DecimalRoundExt für Extended Values. Ich habe sie oben verlinkt und kann sie empfehlen, wenn jemand auch dieses nervige 1 Cent-Problem hat.
Albert
Live long and prosper


MrSpock
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:35 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz