Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Ceil Floor Log10 - Rundungsfehler, Überlauf o.ä. (?) (https://www.delphipraxis.net/160534-ceil-floor-log10-rundungsfehler-ueberlauf-o-ae.html)

idefix2 18. Mai 2011 23:56

AW: Ceil Floor Log10 - Rundungsfehler, Überlauf o.ä. (?)
 
Zitat:

Deine Idee ist zwar ein Disaster-Konzept, weil Differenzen von zwei Sprungfunktionen geradezu danach schreien, solche Effekte hervorzurufen
Das ist der springende Punkt: Gleitkommaarithmetik kann von der Systematik her keine ganz exakten Werte liefern, und schon gar nicht, wenn wie hier eine transzendentale Funktion wie der Dezimallogarithmus aufgerufen wird. MINIMALE Abweichungen vom mathematisch exakten Ergebnis sind da nicht überraschend, sonden völlig normal und zu erwarten. Diese Abweichungen sind in einer Grössenordnung, die normalerweise keine Rolle spielt.

Wenn Du jetzt aber als Ergebnis einer solchen Funktion eine ganze Zahl erwartest, und es kommt etwas heraus, das im Rahmen der erlaubten Abweichung kleiner ist als die erwartete Zahl, dann wirkt sich die danach angewendete Funktion Floor natürlich katastrophal aus, weil der Dezimalteil, in dem Fall vielleicht 0.99999999999999999999999999, abgeschnitten wird. Daß das aber kein Fehler von Delphi, sondern ein Fehler im Programmierkonzept ist, muss jedem klar sein, der sich auch nur im Entfertnesten mit numerischer Mathematik und der Rundungsproblematik beschäftigt hat.

Zitat:

Siehe da: Dort kommt das richtige Ergebnis raus.
Ob das Ergebnis der log10 Funktion 7.000000000000000001 oder 7.000000000000000000 oder 6.999999999999999999999 liefert, ist ein reines Lotteriespiel - in den beiden ersten Fällen liefert floor(log10(x)) dann die erwartete 7, im anderen Fall eben 6. Das Ergebnis ist deshalb nicht in einem Fall richtig und im anderen Fall falsch (in Hinblick auf die Umsetzung durch den Compiler), sondern in allen Fällen im Rahmen der von einer Gleitkommaarithmetik erwartbaren Genauigkeit.

Das Problem wirst Du auch nicht los, indem Du die Rechengenauigkeit erhöhst, weil, egal auf wieviele Stellen Du rechnest, in der allerletzten Binärstelle eine Abweichung sein wird, und ganz egal wie klein die Abweichung ist, wenn sie zufällig nach unten geht, schneidet floor den Dezimalteil weg.

hansmaad 19. Mai 2011 08:23

AW: Ceil Floor Log10 - Rundungsfehler, Überlauf o.ä. (?)
 
Also erstmal Danke für die hilfreichen Hinweise.
Vielleicht können wir dann ja noch klären, was die richtige Implementierung für das Problem ist. Gesucht ist eine Funktion, die die nächst niedrige und eine Funktion, die die nächst höhere Zehnerpotenz zu einer Gleitkommazahl zurückgibt. Und zwar als Ganzzahl, die den Exponenten angibt.

Jens01 19. Mai 2011 10:57

AW: Ceil Floor Log10 - Rundungsfehler, Überlauf o.ä. (?)
 
Delphi-Quellcode:
Floor(Round(Log10(min)))

idefix2 19. Mai 2011 11:14

AW: Ceil Floor Log10 - Rundungsfehler, Überlauf o.ä. (?)
 
Es gibt zu Deiner Frage zwei Probleme, die man berücksichtigen muss:
Das erste hast Du im Ursprungsposting angesprochen, nämlich die Genauigkeit der Funktion selbst. Die Funktion ist nichtlinear, sie hat bei jeder Zehnerpotenz eine Sprungstelle. Nachdem auch Gleitkommaarithmetik exakt rechnet, solange nur ganze Zahlen und die Grundrechenarten involviert sind, kannst Du in einer Schleife abfragen, ob Dein x grösser als 1,10,100 etc. ist, eine einfache Multiplikation einer ganzen Realzahl mit 10 wird sicher keine Rundungsfehler produzieren. Bei den Dezimalzahlen 0.1, 0.01 ist die Angabe des Sprungpunkts aber nicht exakt möglich, weil diese Zahlen in Binärdarstellung eine periodische Mantisse haben.

Damit hast Du aber das zweite Problem noch nicht gelöst, nämlich die Genauigkeit des Funktionsparameters x selbst: Wo kommt der her? Wenn der das Ergebnis einer Geitkommaberechnung ist, dann kann sich auch dort der kleinste Rundungsfeher dahingehend auswirken, dass bei einer Zahl, die z.B. ganz knapp kleiner als 100 sein sollte, etwas herauskommt, was ganz knapp grösser als 100 ist und deshalb das Ergebnis der Gesamtberechnung einen unerwarteten Wert liefert. Oder anders herum, der Parameter, den Du übergibst, hätte genau 10 sein sollen, ist aber als Ergebnis einer Gleitkommarechnung 9.999999999999999999999999999999994, und das Ergebnis Deiner Funktion stimmt deshalb nicht mit dem, was Du erwartest, überein, obwohl die Funktion selbst ganz genau und ohne Rundungsfehler rechnet.

Zitat:

Floor(Round(Log10(min)))
Das wird ganz falsche Ergebnisse bringen, floor(round(0.7))=1 statt der erwarteten 0. Floor auf einen gerundeten Wert anzuwenden ist übrigens sinnlos, weils der gerundete Wert ja schon eine ganze Zahl ist und der nachfolgende floor-Aufruf nichts mehr verändert.

gammatester 19. Mai 2011 11:47

AW: Ceil Floor Log10 - Rundungsfehler, Überlauf o.ä. (?)
 
Hier ein Vorschlag, der mit einer Toleranz für den log10 arbeitet. Die Funktionen schauen nach, ob x nahe bei einer Zehnerpotenz liegt. Wenn nein, werden die nächst niedrigeren bzw. höheren Potenzen genommen. Die Nähe-Toleranz tol sollte sinnvollerweise nicht kleiner als ca 1e-15 gewählt werden.
Delphi-Quellcode:
{---------------------------------------------------------------------------}
function minpower10(x: extended): integer;
  {-Berechnet für x>0 den maximalen Integerwert mit 10^result <= x}
const
  tol = 1e-9;
var
  lx: extended;
  li: integer;
begin
  lx := log10(x);
  li := round(lx);
  if abs(li - lx) > tol then li := floor(lx);
  minpower10 := li;
end;


{---------------------------------------------------------------------------}
function maxpower10(x: extended): integer;
  {-Berechnet für x>0 den minimalen Integerwert mit 10^result >= x}
const
  tol = 1e-9;
var
  lx: extended;
  li: integer;
begin
  lx := log10(x);
  li := round(lx);
  if abs(li - lx) > tol then li := ceil(lx);
  maxpower10 := li;
end;

idefix2 19. Mai 2011 11:55

AW: Ceil Floor Log10 - Rundungsfehler, Überlauf o.ä. (?)
 
Das geht einfacher, indem Du für die Funktion floor einfach 1E-9 zum Parameter addierest und für die Funktion Ceil einfach 1E-9 vom Parameter abziehst.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:20 Uhr.
Seite 2 von 2     12   

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