Autor Beitrag
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 08.08.13 19:36 
Moin!

Ich arbeite mich grade für Tau durch einige Untiefen der Fließkommaarithmetik. Unter anderem lese ich dazu auch in den Quellen der AMath von user profile iconGammatester und hoffe, er liest hier auch mit ;)

Jedenfalls: dort definiert er:
ausblenden Delphi-Quelltext
1:
2:
{Machine epsilons: smallest (negative) powers of 2 with 1 + eps_? <> 1}
const eps_x: extended = 1.084202172485504434E-19{Hex: 0000000000000080C03F}

In dieser Größenordnung ist auch das auf 1E-19 gerundete ExtendedEpsilon aus der normalen Math.pas. Allerdings: dieser Wert scheint nicht korrekt zu sein, wie ich eben mal überprüft habe (sieht mit halben Leerzeichen besser aus ;-)):
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
>  table(each(range(-50,-70,-1),n, {n, 2^n, 1 + 2^n} ))
....
 -55  2.775 557 561 562 891 35E-17  1.000 000 000 000 000 03
 -56  1.387 778 780 781 445 68E-17  1.000 000 000 000 000 01
 -57  6.938 893 903 907 228 38E-18  1.000 000 000 000 000 01
 -58  3.469 446 951 953 614 19E-18  1
 -59  1.734 723 475 976 807 09E-18  1
 -60  8.673 617 379 884 035 47E-19  1
 -61  4.336 808 689 942 017 74E-19  1
 -62  2.168 404 344 971 008 87E-19  1
 -63  1.084 202 172 485 504 43E-19  1
....

2^-63 ist zwar an sich klar wegen 64bit Sign+Exponent, aber das scheint sich auf die eigentliche Rechenoperation nicht so durchzuschlagen.


Delphi definiert ja gleich mal einen FuzzFactor von 1E3 dazu (vermutlich aus diesem Grund), aber der versaut mir grade die Division, da IsZero schon bei ziemlich großen Zahlen der Meinung ist, das wäre eine Null, so dass im Endeffekt mein Code dann 1.6E-19 / 1.6E-19 als 0/0 "erkennt":
ausblenden Quelltext
1:
2:
>  1_'eV' / const('ElectronCharge')
=   NAN
Ignoriert diesen Absatz. Das war Background, provoziert aber irrelevante Antworten.

Was habe ich hier nicht verstanden?

Viele Grüße,
Martok

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."


Zuletzt bearbeitet von Martok am Do 08.08.13 22:21, insgesamt 2-mal bearbeitet
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: Do 08.08.13 21:29 
Hallo Martok,
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
... aber der versaut mir grade die Division, da IsZero schon bei ziemlich großen Zahlen der Meinung ist, das wäre eine Null, so dass im Endeffekt mein Code dann 1.6E-19 / 1.6E-19 als 0/0 "erkennt"

Es hilft Dir zwar nicht wirklich weiter, aber ich habe Folgendes getestet und es funktioniert ohne Arithmetikprobleme, d.h. jedes Mal ergibt sich 1.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm1.Button1Click(Sender: TObject);
const
    eps_y = 1.084202172485504434E-19;
var
    eps_x,ergebnis:extended;
begin
    eps_x:=1.084202172485504434E-19;
    ergebnis:=eps_x/1.084202172485504434E-19;
    memo1.lines.add(floattostr(ergebnis));
    ergebnis:=1.084202172485504434E-19/1.084202172485504434E-19;
    memo1.lines.add(floattostr(ergebnis));
    ergebnis:=eps_x/eps_y;
    memo1.lines.add(floattostr(ergebnis));
    ergebnis:=eps_y/1.084202172485504434E-19;
    memo1.lines.add(floattostr(ergebnis));
    ergebnis:=eps_y/eps_y;
    memo1.lines.add(floattostr(ergebnis));
    ergebnis:=1.084202172485504434E-19/eps_y;
    memo1.lines.add(floattostr(ergebnis));
end;

Dann habe ich E-19 gegen E-100 und noch einmal gegen E-4950 getauscht; wieder keine Probleme. Erst bei E-4951 gab es eine 0/0, genau nach Beschreibung des Extended-Typs.
Auch die math-Funktionen sin, cos, tan, sqrt und ln arbeiten bei E-4950 noch ohne Schwierigkeiten.
Könnte es sein, dass der Fehler wo anders liegt. Allerdings spekuliere ich nur.

Beste Grüße
Mathematiker

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 08.08.13 22:17 
Moin!

Ja, das ist ein komplett anderes Thema. Das Epsilon in der Nähe von 0 hat so genau gar nix mit der darstellbaren Präzision zu tun. Große Exponenten sind kein Thema, interessant wird es erst bei Operationen bei denen sich die Exponenten um mehr als eine bestimmte Stellenzahl unterscheiden. Bei Extended so ungefähr wenn |lg(x) - lg(y)| >= 19.
Der letzte Absatz (ich ärgere mich jetzt, ihn verwendet zu haben) war mehr zur Erklärung, warum ich mir das antue. Die Frage steht davor (und danach) ;)
Aber wenn ihr es wissen wollt: Das tu ich damit. Ist hier aber nicht das Thema. Ich weiß mittlerweile, dass ich IsZero nicht verwenden sollte sondern eher die Größenordnungen vergleichen muss ;)

Grüße,
Martok

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Fr 09.08.13 09:09 
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:

Ist schon etwas komisch. In der Delphi 5-Hilfe steht
Zitat:
Extended 3.6 x 10^–4951 .. 1.1 x 10^4932

was auch funktioniert.
Unter www.delphibasics.co....TL.asp?Name=Extended steht
Zitat:
It supports approximately 19 digits of precision in a range from 3.37 x 10^-4932 to 1.18 x 10^4932.


Das ist beides richtig :wink:

Allerdings ist 3.6 x 10^–4951, genauer 3.64519953188247460E-4951 =2^(-16445) die kleinste positive denormalisierte Extendedzahl, also die kleinste Zahl größer als 0. Andererseits ist 3.37 x 10^-4932, genauer 3.362103143112093507E-4932 = 2^(-16382) die kleinste positive normalisierte Extendedzahl (alle Zahlen kleiner sind also denormal).

Für diesen Beitrag haben gedankt: Mathematiker
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Fr 09.08.13 10:42 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Der letzte Absatz (ich ärgere mich jetzt, ihn verwendet zu haben) war mehr zur Erklärung, warum ich mir das antue. Die Frage steht davor (und danach) ;)
Ich weiß zwar immer noch nicht genau, was die Frage ist, aber die Antwort ist 42. Vielleicht entstehen ein paar Probleme durch die mangelnde Genauigkeit bei der Dezimalausgabe (die leider auf ca 17 Stellen beschränkt ist). Hier mal eine entsprechende Tabelle mit Hexdarstellung (mit der AMath-Funktion Ext2Hex) der Summe und als Zugabe auch für Differenzen: Man beachte 1 + 0.5*eps_x = 1 aber 1 - 0.5*eps_x <> 1:
ausblenden 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:
  i                         2^i           1+2^i (dec)           1+2^i (hex)
-55   2.77555756156289135E-0017  1.000000000000000030  3FFF8000000000000100
-56   1.38777878078144568E-0017  1.000000000000000010  3FFF8000000000000080
-57   6.93889390390722838E-0018  1.000000000000000010  3FFF8000000000000040
-58   3.46944695195361419E-0018  1.000000000000000000  3FFF8000000000000020
-59   1.73472347597680709E-0018  1.000000000000000000  3FFF8000000000000010
-60   8.67361737988403547E-0019  1.000000000000000000  3FFF8000000000000008
-61   4.33680868994201774E-0019  1.000000000000000000  3FFF8000000000000004
-62   2.16840434497100887E-0019  1.000000000000000000  3FFF8000000000000002
-63   1.08420217248550443E-0019  1.000000000000000000  3FFF8000000000000001
-64   5.42101086242752217E-0020  1.000000000000000000  3FFF8000000000000000
-65   2.71050543121376108E-0020  1.000000000000000000  3FFF8000000000000000

  i                         2^i           1-2^i (dec)           1-2^i (hex)
-55   2.77555756156289135E-0017  0.999999999999999970  3FFEFFFFFFFFFFFFFE00
-56   1.38777878078144568E-0017  0.999999999999999990  3FFEFFFFFFFFFFFFFF00
-57   6.93889390390722838E-0018  0.999999999999999990  3FFEFFFFFFFFFFFFFF80
-58   3.46944695195361419E-0018  1.000000000000000000  3FFEFFFFFFFFFFFFFFC0
-59   1.73472347597680709E-0018  1.000000000000000000  3FFEFFFFFFFFFFFFFFE0
-60   8.67361737988403547E-0019  1.000000000000000000  3FFEFFFFFFFFFFFFFFF0
-61   4.33680868994201774E-0019  1.000000000000000000  3FFEFFFFFFFFFFFFFFF8
-62   2.16840434497100887E-0019  1.000000000000000000  3FFEFFFFFFFFFFFFFFFC
-63   1.08420217248550443E-0019  1.000000000000000000  3FFEFFFFFFFFFFFFFFFE
-64   5.42101086242752217E-0020  1.000000000000000000  3FFEFFFFFFFFFFFFFFFF
-65   2.71050543121376108E-0020  1.000000000000000000  3FFF8000000000000000

Für diesen Beitrag haben gedankt: Martok
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Fr 09.08.13 15:21 
Danke

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Vielleicht entstehen ein paar Probleme durch die mangelnde Genauigkeit bei der Dezimalausgabe (die leider auf ca 17 Stellen beschränkt ist)
Das scheint auf jeden Fall ein Teil des Problems zu sein, in Memdump-Darstellung kann ich deine Tabelle 1:1 reproduzieren. Das Phänomen der Dezimal-Darstellung wird eher seltsamer ;-)
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
>  table(each(range(-55,-65,-1),n,{n, 2^n, 1+2^n, exthex(1+2^n), 1-2^n, exthex(1-2^n) }))
-55  2.77555756156289135E-17  1.00000000000000003  3FFF8000000000000100  0.999999999999999972  3FFEFFFFFFFFFFFFFE00
-56  1.38777878078144568E-17  1.00000000000000001  3FFF8000000000000080  0.999999999999999986  3FFEFFFFFFFFFFFFFF00
-57  6.93889390390722838E-18  1.00000000000000001  3FFF8000000000000040  0.999999999999999993  3FFEFFFFFFFFFFFFFF80
-58  3.46944695195361419E-18  1                    3FFF8000000000000020  0.999999999999999996  3FFEFFFFFFFFFFFFFFC0
-59  1.73472347597680709E-18  1                    3FFF8000000000000010  0.999999999999999998  3FFEFFFFFFFFFFFFFFE0
-60  8.67361737988403547E-19  1                    3FFF8000000000000008  0.999999999999999999  3FFEFFFFFFFFFFFFFFF0
-61  4.33680868994201774E-19  1                    3FFF8000000000000004  1                     3FFEFFFFFFFFFFFFFFF8


1+2^n ergibt genau deine Werte allerds mit einer Stelle weniger, 1-2^n gibt mehr und andere Stellen als dein Beispiel.
Ich verwende FloatToStrF(Value, ffGeneral, 2218, NeutralFormatSettings); (aha, da kommen die 18 Stellen her :idea:). Was hast du aufgerufen?

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 12.08.13 09:10 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Das Phänomen der Dezimal-Darstellung wird eher seltsamer ;-)
...
Ich verwende FloatToStrF(Value, ffGeneral, 2218, NeutralFormatSettings); (aha, da kommen die 18 Stellen her :idea:). Was hast du aufgerufen?
In der Tat, aber es wird noch seltsamer :!: Meine Werte wurden mit folgendem Programm erzeugt:
ausblenden 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:
program t_eps;
{$i STD.INC}
{$ifdef AppCons}
  {$apptype console}
{$endif}
{$ifdef BIT16}
{$N+}
{$endif}
uses
  amath;
var
  x,y: extended;
  i: integer;
begin
  writeln('i':3'2^i':28'1+2^i (dec)':22'1+2^i (hex)':22);
  for i:=55 to 65 do begin
    x := ldexp(1,-i);
    y := 1 + x;
    writeln(-i:3, x:28, y:22:18, Ext2Hex(y):22);
  end;
  writeln;
  writeln('i':3'2^i':28'1-2^i (dec)':22'1-2^i (hex)':22);
  for i:=55 to 65 do begin
    x := ldexp(1,-i);
    y := 1 - x;
    writeln(-i:3, x:28, y:22:18, Ext2Hex(y):22);
  end;
end.
Deine Werte kann ich mit TurboPascal 5 - 7 und Delphi 17/18 reproduzieren, Delphi 2 - 12 liefern meine oben genannten Werte (mit D6).

Hoffentlich interessiert's noch (die langen Wochenenden ...)

Für diesen Beitrag haben gedankt: Martok
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mo 12.08.13 16:23 
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Hoffentlich interessiert's noch (die langen Wochenenden ...)
Klar doch :) Damit haben wir immerhin 4 OnTopic-Beiträge in diesem Thread :lol:

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
In der Tat, aber es wird noch seltsamer :!:
--Schnipp--
Deine Werte kann ich mit TurboPascal 5 - 7 und Delphi 17/18 reproduzieren, Delphi 2 - 12 liefern meine oben genannten Werte (mit D6).
Heißt das, wir haben grade einen Bug in AMath gefunden? ;-) Bei mir wars ja die normale Math (also deren Power) und D7.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 12.08.13 16:38 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Heißt das, wir haben grade einen Bug in AMath gefunden? ;-) Bei mir wars ja die normale Math (also deren Power) und D7.
Nein, das ist eher ein Feature der Ausgabe mit writeln in D2-D12 (keine Ahnung, was die intern benutzen). Aber man sollte das nicht zu verbissen sehen und als Bug bezeichnen, ob 17 oder 18 Stellen ist fast schon egal, wenn selbst 60 nicht ausreichen um exakt zu sein (hier 1 +- 2^-63):
ausblenden Quelltext
1:
2:
1.00000000000000000010842021724855044340074528008699417114258...
0.99999999999999999989157978275144955659925471991300582885742...
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mo 12.08.13 18:42 
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Aber man sollte das nicht zu verbissen sehen und als Bug bezeichnen, ob 17 oder 18 Stellen ist fast schon egal, wenn selbst 60 nicht ausreichen um exakt zu sein (hier 1 +- 2^-63):
Wohl wahr.
Ich würde ja auf mpf oder sowas umbauen, aber deine TP-Kompatibilitätsverrenkungen führen leider dazu, dass mir der Code so gar nicht gefällt :nixweiss:

Muss ich halt damit leben. Das mittlerweile oben durchgestrichene Beispiel umgehen wir dann mal durch assembler und eigene FPE-Behandlung, dann brauche ich nicht vorher auf 0 zu prüfen ;)

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Di 13.08.13 18:08 
Moderatives Zeugs
Achtung, ich hab mal dieses Thema etwas aufgeräumt und die Beiträge zu Extended mit 64bit-Delphi in einen eigenen Thread verschoben.

Das führt dazu, dass hier ein Beitrag von Mathemtiker fehlt. Der relevante Teil wurde aber in der Antwort von Gammatester zitiert, man kann dem Thema also noch folgen. Genau deswegen haben wir die Nur-Eine-Frage-Pro-Thema-Regel 8)

/Moderatives Zeugs

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mi 14.08.13 02:18 
So, und noch was zum Thema. So langsam verstehe ich mehr als Borland damals, hab ich den Verdacht :lol:

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Andererseits ist 3.37 x 10^-4932, genauer 3.362103143112093507E-4932 = 2^(-16382) die kleinste positive normalisierte Extendedzahl (alle Zahlen kleiner sind also denormal).
Mit anderen Worten: IsZero an sich ist keine dumme Idee, aber damals komplett sinnfrei implementiert worden. Das Kriterium hätte eigentlich sein müssen, "Wird 1/x eine ZE auslösen?" und die Antwort wäre also "ist die Zahl zwischen 2^-16382 und -2^16383", oder?
Könnte man ja reparieren.

€:/ Fun Fact: jetzt hab ich schon einen Bug in meiner CPU gefunden: SIN/COS verwenden offensichtlich unterschiedliche Tabellen, die bekannten Punkte (Nullstellen, +/-1) verhalten sich anders...

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mi 14.08.13 09:56 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Mit anderen Worten: IsZero an sich ist keine dumme Idee, aber damals komplett sinnfrei implementiert worden. Das Kriterium hätte eigentlich sein müssen, "Wird 1/x eine ZE auslösen?" und die Antwort wäre also "ist die Zahl zwischen 2^-16382 und -2^16383", oder?
Könnte man ja reparieren.
Nein, dafür ist IsZero meiner Meinung nicht primär gedacht, eher für Differenzabschätzungen. Man muß auch zwischen der Implementierung und der Anwendung von IsZero unterscheiden. Die Implementierung
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
function IsZero(const A: Extended; Epsilon: Extended): Boolean;
begin
  if Epsilon = 0 then
    Epsilon := ExtendedResolution;
  Result := Abs(A) <= Epsilon;
end;
ist freundlich ausgedrückt dunkel mit dem FuzzFactor = 1000 und den zT willkürlichen Resolution-Konstanten:
ausblenden Quelltext
1:
2:
3:
SingleResolution   = 1E-7 * FuzzFactor;
DoubleResolution   = 1E-15 * FuzzFactor;
ExtendedResolution = 1E-19 * FuzzFactor;
Während 1e-19 noch ca 1*eps_x ist, ist aber 1e-15 ca 5*eps_d! Weiter hätte man if Epsilon <= 0 schreiben sollen, aktuell ist eine echte 0 nicht Zero mit Epsilon=-1e-10!

Aber nicht jammern, wenn die Welt mit Delphi und SI-Einheiten programmiert wäre, würde alles viel einfacher sein (allerdings nicht unbedingt besser), da sperrige Konstanten wegfallen: u.a. Boltzmann, Planck (hurra, keine Quanteneffekte mehr!), Elementarladung (nicht so gut, keine Computer mehr).

Schlimm ist allerdings die Anwendung von IsZero in der RTL, Paradebeispiel dafür ist mal wieder die total blamable Implementation von sinh/tanh (tanh hier für D7-D12, in D17+ besser):
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
function Sinh(const X: Extended): Extended;
begin
  if IsZero(X) then
    Result := 0
  else
    Result := (Exp(X) - Exp(-X)) / 2;
end;

function Tanh(const X: Extended): Extended;
begin
  if IsZero(X) then
    Result := 0
  else
    Result := SinH(X) / CosH(X);
end;
Wie viele Gymnasiasten wissen (sollten), gilt für kleine Argumente sin(x) ~ x, sinh(x) ~ x, tanh(x) ~ x (Stichwort Taylor). Etwas besser (und selbstverständlich) genauer wäre also
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
function Sinh(const X: Extended): Extended;
begin
  if IsZero(X) then
    Result := X
  else
    Result := (Exp(X) - Exp(-X)) / 2;
end;
Warum sich das nicht bis EMBA rumgesprochen hat, weiß der Geier. Und warum für tanh(x) dann noch 4 (in Worten vier) Mal exp aufgerufen werden muß, ist auch unklar, ganz zu schweigen vom völlig uberflüssigen Crash für tanh(12345) in D7-D12, was ja bekanntermaßen = 1 ist. Fehlt eigentlich nur noch, daß man die gleiche Logik auf sin(x) und tan(x) anwendet.
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
€:/ Fun Fact: jetzt hab ich schon einen Bug in meiner CPU gefunden: SIN/COS verwenden offensichtlich unterschiedliche Tabellen, die bekannten Punkte (Nullstellen, +/-1) verhalten sich anders...
Interessant, kannst Du mehr dazu sagen?

PS_1: Deine Absicht, Divisionsüberläufe zu vermeiden, kann man wie Du schon erwähnt hast mit try..except abfangen, oder aber zB wie folgt umgehen: Wenn x/y im Verdacht steht überzulaufen, hilft die Anweissung
ausblenden Delphi-Quelltext
1:
2:
3:
if (abs(y)<1.0and (abs(x) >= abs(y)*MaxExtended) then begin
  //Überlaufbehandlung, Meldung etc
end;

Edit PS_2: Auf mögliche Antworten auf Deine möglichen Antworten must Du leider wegen des ultralangen bayrischen Wochenende bis Montag warten :cry:
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 15.08.13 00:22 
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Edit PS_2: Auf mögliche Antworten auf Deine möglichen Antworten must Du leider wegen des ultralangen bayrischen Wochenende bis Montag warten :cry:
:bawling: Dann mach ich das Release halt ohne Antwort :rofl:

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Nein, dafür ist IsZero meiner Meinung nicht primär gedacht, eher für Differenzabschätzungen.
Joa, das ist dann ja das was SameValue=IsZero(A-B) macht.

Meistens geht es doch aber darum, dass IsZero aufgerufen wird um die DivZero oder den Ln(0) zu vermeiden. Und dafür wäre ja dann halt die erste normalisierte Zahl <> 0 Maßgeblich, nicht irgendein Vergleichsepsilon. Und selbst wenn, müsste man doch das auf die Zahlen an sich beziehen, also z.B.: A = B <=> (A-B) / A < 1E-19.

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Schlimm ist allerdings die Anwendung von IsZero in der RTL, Paradebeispiel dafür ist mal wieder die total blamable Implementation von sinh/tanh (tanh hier für D7-D12, in D17+ besser): Wie viele Gymnasiasten wissen (sollten), gilt für kleine Argumente sin(x) ~ x, sinh(x) ~ x, tanh(x) ~ x (Stichwort Taylor).

Das hab ich dann gleich mal ordentlich nachgebaut.
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
PS_1: Deine Absicht, Divisionsüberläufe zu vermeiden, kann man wie Du schon erwähnt hast mit try..except abfangen
... oder das die FPU machen lassen, z.B. so.

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
€:/ Fun Fact: jetzt hab ich schon einen Bug in meiner CPU gefunden: SIN/COS verwenden offensichtlich unterschiedliche Tabellen, die bekannten Punkte (Nullstellen, +/-1) verhalten sich anders...
Interessant, kannst Du mehr dazu sagen?
Ich hab mal ein paar Werte berechnet von Sin/Cos im Abstand von Pi/4 (bzw an den Stellen direkt, die Hex-Darstellung hab ich von dir). Sin() liefert wie zu erwarten {0, 1, 0, -1}, aber Cos(Pi/4) ist nicht 0, sondern ~1E-19. Man kann mit anderer Hex-Darstellung ($...c234 ... $...c237) noch ein Minimum bei ~1E-20 finden, aber exakt 0 bzw irgendwas subnormales wirds nicht. Man sollte doch annehmen, dass die eine nur die um Pi/4 verschobene andere Funktion ist. Scheint aber nicht so.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 19.08.13 10:08 
Moin, bin wieder online!
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Das hab ich dann gleich mal ordentlich nachgebaut.
Eigentlich hättest Du es doch gleich besser machen können, also nur nur 'iszero(x) -> sinh := x', so hast Du immer noch die 'katastrophale Auslöschung' in Result := (Exp(X) - Exp(-X)) / 2; für kleine x, die größer als die IsZero-Grenze sind, zB für
ausblenden Quelltext
1:
2:
3:
x =  1.00000000000000000E-0008
Simple     1.00000000000219271E-0008    -2.19269459360293961E-0012
 AMath     1.00000000000000001E-0008     0.00000000000000000E+0000
Der erste Wert ist sinh(x), der zweite der relative Fehler.
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Ich hab mal ein paar Werte berechnet von Sin/Cos im Abstand von Pi/4 (bzw an den Stellen direkt, die Hex-Darstellung hab ich von dir). Sin() liefert wie zu erwarten {0, 1, 0, -1}, aber Cos(Pi/4) ist nicht 0, sondern ~1E-19. Man kann mit anderer Hex-Darstellung ($...c234 ... $...c237) noch ein Minimum bei ~1E-20 finden, aber exakt 0 bzw irgendwas subnormales wirds nicht. Man sollte doch annehmen, dass die eine nur die um Pi/4 verschobene andere Funktion ist. Scheint aber nicht so.
Irgendwie scheinst Du da systematisch Pi/4 mit Pi/2 zu verwechseln, glücklicherweise ist cos(Pi/4) nicht 0 sondern sqrt(0.5) und cos(x) = sin(x+Pi/2), ich führe dies allerdings auf die fortgeschrittene Erstellungszeit Deines Beitrags zurück :P Im Übrigen bezweifele ich, daß für den Extended-Wert von Pi der sin gleich 0 sein soll (Test system: -5.42101086242752217E-0020, AMath: -5.01655761266833202E-0020), es sei denn Du rechnest symbolisch, wenn nicht ist ein Ergebniswert=0 falsch!

Soweit ich mich erinnere (irgendwelche Franzosen haben das mW ausgeknobelt, finde den Artikel gerade nicht), gibt es keine Double-Zahl x für die cos(x) exakt = 0 ist, man kann also getrost immer tan(x) = sin(x)/cos(x) berechnen; vermutlich gilt das auch für extended. Für die Umgebung von Pi/2 finde ich für cos(x) mit MPArith
ausblenden Quelltext
1:
2:
3:
4:
5:
1.570796326794896620  3FFFC90FDAA22168C233  +1.917576464337592266837039248E-19
1.570796326794896620  3FFFC90FDAA22168C234  +8.333742918520878328295864468E-20
1.570796326794896620  3FFFC90FDAA22168C235  -2.508278806334166011778663540E-20
1.570796326794896620  3FFFC90FDAA22168C236  -1.335030053118921035185319155E-19
1.570796326794896620  3FFFC90FDAA22168C237  -2.419232225604425469192771956E-19
Der Extendedwert für Pi/2 ist die mittlere Zeile und hat (wie erwartet) den kleinsten Funktionswert.
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mo 19.08.13 16:28 
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Moin, bin wieder online!
:wave:

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Eigentlich hättest Du es doch gleich besser machen können, also nur nur 'iszero(x) -> sinh := x', so hast Du immer noch die 'katastrophale Auslöschung' in Result := (Exp(X) - Exp(-X)) / 2; für kleine x, die größer als die IsZero-Grenze sind
Ähm, wie meinen? Ich versteh jetzt grade nicht was du mir als Alternative vorschlagen möchtest. In diesem Satz ist ein Wort übrig ;)

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Irgendwie scheinst Du da systematisch Pi/4 mit Pi/2 zu verwechseln
:oops: Ich weiß schon warum ich lieber mit tau rechne :lol:

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Soweit ich mich erinnere (irgendwelche Franzosen haben das mW ausgeknobelt, finde den Artikel gerade nicht), gibt es keine Double-Zahl x für die cos(x) exakt = 0 ist, man kann also getrost immer tan(x) = sin(x)/cos(x) berechnen; vermutlich gilt das auch für extended.
Scheint so. Das könnte wirklich Absicht für den tan sein. Seltsamerweise kann ich das jetzt auch nicht mehr reproduzieren außer bei sin(0)=0 und da wird es wohl eine Sonderfallbehandlung sein. Ansonsten ähnlich wie wir schon kennen:
ausblenden Quelltext
1:
2:
>  sin(exthex('4000C90FDAA22168C235'))
=  -5.421 010 862 427 522 17E-20

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Für die Umgebung von Pi/2 finde ich für cos(x) mit MPArith {...} Der Extendedwert für Pi/2 ist die mittlere Zeile und hat (wie erwartet) den kleinsten Funktionswert.
Ja, das sind die Zahlenwerte die ich auch finde :zustimm:

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Gammatester
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 328
Erhaltene Danke: 101



BeitragVerfasst: Mo 19.08.13 16:42 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Eigentlich hättest Du es doch gleich besser machen können, also nur nur 'iszero(x) -> sinh := x', so hast Du immer noch die 'katastrophale Auslöschung' in Result := (Exp(X) - Exp(-X)) / 2; für kleine x, die größer als die IsZero-Grenze sind
Ähm, wie meinen? Ich versteh jetzt grade nicht was du mir als Alternative vorschlagen möchtest. In diesem Satz ist ein Wort übrig ;)
Nicht übrig sondern falsch, d.h. ersetze 'nur nur' durch 'nicht nur'. Ansonsten kann ich nur auh AMath verweisen:
ausblenden volle Höhe 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:
35:
36:
37:
function sinh(x: extended): extended;
  {-Return the hyperbolic sine of x, accurate even for x near 0}
var
  t: extended;
const
  t0 = 23.0;     {ceil(-0.5*ln(2^-64)) = ceil(32*ln(2))}
  t1 = 2.3E-10;  {~ sqrt(2^-64)}
begin
  t := abs(x);
  if t<=t1 then begin
    {sinh(x) = x*(1 + 1/6*x^2 + 1/120*x^4 + O(x^6))}
    sinh := x;
  end
  else begin
    {sinh(t) = 0.5*expm1(t)*(1+exp(-t) = 0.5*y(1+1/(1+y))}
    {        = 0.5*y*(2-y/(y+1)) = t-0.5*t*t/(1+t) with y = expm1(t)}
    if t<ln2 then begin
      t := expm1(t);
      t := t*(1.0-0.5*t/(1.0+t));
    end
    else if t<=t0 then begin
      {calculate inverse only if it is not too small}
      t := exp(t);
      t := 0.5*(t - 1.0/t);
    end
    else if t<ln_MaxExt then begin
      {t>t0: exp(t)-exp(-t) ~ exp(t)}
      t := 0.5*exp(t)
    end
    else begin
      {exp(t) would overflow, use small margin below overflow}
      t := exp(t-ln2);
    end;
    if x>0.0 then sinh := t
    else sinh := -t;
  end;
end;
Aber es gibt natürlich auch andere genauere Implementationen.
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mo 19.08.13 19:31 
Achso :)

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Ansonsten kann ich nur auh AMath verweisen:
Hatte ich auch schon gelesen und als "irgendwie kompliziert" wieder weggelegt ;-)

user profile iconGammatester hat folgendes geschrieben Zum zitierten Posting springen:
Aber es gibt natürlich auch andere genauere Implementationen.
Spannenderweise hatte man da bei Mathematica auch keine Lust auf Optimierung bei 0. Für große Zahlen funktioniert das tatsächlich mit arbitrary precision, kleine Zahlen werden aber nicht besser als MachinePrecision, wenn überhaupt.

"Braucht eh keiner" :lol:

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."