Einzelnen Beitrag anzeigen

Medium

Registriert seit: 23. Jan 2008
3.686 Beiträge
 
Delphi 2007 Enterprise
 
#31

AW: tan() von Single, Double, etc.

  Alt 21. Nov 2017, 12:48
Wie gesagt, die Gleitkomma-Arithmetik ist exakt, genau so exakt wie Integer-Arithmetik. Was sollte denn die tan(90°) (Eingabe als Radian, natürlich Uwe) deiner Meinung nach zurückgeben ?
Der richtige Wert wäre vielleicht MAX_SINGLE als größtmögliche Näherung an Infinity ?
Obwohl ich bei tan(90°) nicht meine das es überhaupt + oder - Infinity ist, sondern einfach eine undefinierte Polstelle
(Kann mich auch irren. bin jetzt kein Zahlentheoretiker).

Jedenfalls liefert tan(90°) bei mir irgendwas mit -235343.. zurück, also eine konkrete Zahl die unter dem möglichen Max_Single liegt und relativ "zufällig" ist.
Du hast es immer noch nicht ganz verstanden glaube ich. Das hier:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  s: Single;
  d: Double;
  e: Extended;
begin
  s := DegToRad(90);
  d := DegToRad(90);
  e := DegToRad(90);
  Edit1.Text := FloatToStrF(s, ffFixed, 30, 30);
  Edit2.Text := FloatToStrF(d, ffFixed, 30, 30);
  Edit3.Text := FloatToStrF(e, ffFixed, 30, 30);
end;
Das liefert für die drei Variablen:
s = 1,570796370506286620
d = 1,570796326794896560
e = 1,570796326794896620

pi/2 wäre nach diesem Rechner auf 50 Stellen genau: 1.570796326794896619231321691639751442098584699687 6

Die Differenz von dieser Näherung zu pi/2 ist
für s: -0.000000043711390000768678308360248557901415300312 4471
für d: 0.000000000000000059231321691639751442098584699687 553
für e: -0.000000000000000000768678308360248557901415300312 4471

Derselbe Rechner liefert für
tan(s) = -22877332.42942889836981039204072541441251573432
tan(d) = 16882959411340397.91436507298177193134
tan(e) = -1300934329906107203.0757511956816777
(Auch jeweils auf 50 Stellen genau, etwas was eine CPU von Hause aus nicht kann.)

Die Funktion Tan() ist in Delphi (2007) so realisiert:
Delphi-Quellcode:
function Tan(const X: Extended): Extended;
{  Tan := Sin(X) / Cos(X) }
asm
        FLD X
        FPTAN
        FSTP ST(0) { FPTAN pushes 1.0 after result }
        FWAIT
end;
Es wird, wie zu erwarten ist, einfach die tan() Funktion der CPU (bzw. FPU) genutzt, so wie es auch sein sollte für eine Basisfunktionalität. Und hier wird eben mit binären Gleitkommazahlen und Radian gearbeitet, so dass du niemals auch nur die CHANCE hättest tand(90) mit einem 100%ig exakt richtigen Parameter aufzurufen. Und für die zwingend gerundeten Parameter stimmt das Ergebnis das du siehst. Es wäre falsch hier NaN oder +/-Inf zurückzugeben.

Das ist anders für DEINEN Anwendungsfall, in dem du explizit Funktionen in Grad nutzen willst. Das ist aber ein Sonderwunsch, und NICHT Aufgabe eines Compilers bzw. des Basisumfangs einer Entwicklungsumgebung. Im Gegenteil: Ich wäre ziemlich sauer wenn Emba so ein Monster für ein popeliges tan() als Basis hätte wie weiter oben gezeigt, wenn doch die CPU selbst schon eine solche Funktion bietet die für 99,999% aller Echt-Welt-Fälle Werte liefert die den üblichen Ansprüchen genügen. Wer besondere Anforderungen hat, nutzt eben besondere Libs, die entsprechende Fälle anders behandeln; meist auf Kosten der Performance.

Rechnen mit Floats ist immer ungenau, nicht nur bei Trigonometrie. Wenn dich das hier schon stört, müsstest du eigentlich schon weit eher pochende Schläfen bekommen: Es ist nämlich schon manchmal nicht möglich ein exakt eingegebenes Literal in ein Float zu packen:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  s: Single;
begin
  s := 0.01;
  Edit1.Text := FloatToStrF(s, ffFixed, 30, 30);
end;
Ergebnis: 0,009999999776482582

Hier müsstest du eigentlich schon aufgegeben haben. Aber das ist normal. Das ist überall so bei aktuellen üblichen Computern, und das war es auch schon Jahrzehnte lang.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat