AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

tan() von Single, Double, etc.

Ein Thema von Rollo62 · begonnen am 20. Nov 2017 · letzter Beitrag vom 22. Nov 2017
Antwort Antwort
Seite 3 von 5     123 45   
TigerLilly

Registriert seit: 24. Mai 2017
Ort: Wien, Österreich
1.194 Beiträge
 
Delphi 11 Alexandria
 
#21

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

  Alt 21. Nov 2017, 08:40
Ich bewege mich auf dünnem Eis, weil ich ähnliches vor Jahren mal hatte + nicht weiß, ob es das in der Form jetzt immer noch gibt.

Das Problem damals war, dass das Verhalten des mathematische 8087-Coprozessors maskiert werden konnte und dann warf er bestimmte Exceptions oder eben auch nicht.

Im konkreten Fall wurde eine Division durch 0 durch einen Druckertreiber maskiert, dh ohne Drucken wurde die Exception geworfen, nach dem Drucken wurde sie nicht mehr geworfen. :-/ War cool zum Finden.

Vielleicht gibt es für die mathematischen Routinen was ähnliches.
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.064 Beiträge
 
Delphi 12 Athens
 
#22

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

  Alt 21. Nov 2017, 08:57
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.
Wie Uwe schon geschrieben hat, diese Zahl ist womöglich von System zu System verschieden.

Bei meiner Anwendung geht es jetzt nicht unbedingt um hochpräzise Mathematik für Wetterberechnungen o.ä.,
sondern einfach um den praktischen Umgang mit der tan() Funktion die in der Library vorhanden ist.

Klar kann man das Alles je nach Aufruf und Anwendung gesondert abfangen, aber um das möglichst einfache und sichere Abfangen geht es mir ja.
Ich denke schon dass das einfache Weiterrechnen mit dem was tan() mir zurückgibt keine Option sein kann.

Was spräche denn gegen NaN, das ist doch in der Praxis ein klarer Marker für das Resultat das man den Wert nicht verwenden kann/darf ?
Für solche Fälle ist NaN ja wohl auch vorgesehen worden.

Rollo
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#23

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

  Alt 21. Nov 2017, 09:21
Klar kann man das Alles je nach Aufruf und Anwendung gesondert abfangen, aber um das möglichst einfache und sichere Abfangen geht es mir ja.
Ich denke schon dass das einfache Weiterrechnen mit dem was tan() mir zurückgibt keine Option sein kann.

Was spräche denn gegen NaN, das ist doch in der Praxis ein klarer Marker für das Resultat das man den Wert nicht verwenden kann/darf ?
Für solche Fälle ist NaN ja wohl auch vorgesehen worden.
Was genau hält dich auf?

Delphi-Quellcode:
// Eingangswertebereich: -360 bis 360 Grad als Ganzzahl
// Wer das bis ins plus minus Unendliche haben möchte, soll sich das doch selber schnitzen
function tand(const X: Integer): Double;
var
  Rad: Double;
begin
  if (Abs(X) = 90) or (Abs(X) = 270) then
  begin
    Result := System.Math.NaN;
  end
  else
  begin
    Rad := DegToRad(X);
    Result := System.Math.Tan(Rad);
  end;
end;
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#24

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

  Alt 21. Nov 2017, 09:38
Und für diejenigen, die nicht ohne Nachkommastellen auskommen können:

Delphi-Quellcode:
  // Eingangswertebereich: -360.00 bis 360.00 Grad als FLießkommazahl
  // Wer das bis ins plus minus Unendliche haben möchte, soll sich das doch selber stricken
function tand(const X: Double; const Epsilon: Double = 0): Double;
var
  Rad: Double;
begin
  if SameValue(Abs(X), 90.0, Epsilon) or SameValue(Abs(X), 270.0, Epsilon) then
  begin
    Result := System.Math.NaN;
  end
  else
  begin
    Rad := DegToRad(X);
    Result := System.Math.Tan(Rad);
  end;
end;
  Mit Zitat antworten Zitat
gammatester

Registriert seit: 6. Dez 2005
999 Beiträge
 
#25

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

  Alt 21. Nov 2017, 10:49
Richtig, aber so sollte eine tand(x) Funktion mit Argumenten in ° ja auch nicht programmiert werden
Da zum Delphi-Sprachumfang aber nun mal keine tand-Funktion gehört (Delphi 5 kannte noch nicht einmal tan), kann man sich ja bei Bedarf was Passendes selbst programmieren. Mach doch mal einen Vorschlag so wie du es dir vorstellst.
Hier eine vereinfachte Implementation meiner DAMath-Funktion komplett mit Test-Programm
Delphi-Quellcode:
{Test/dev program for tand  (c) W.Ehrhardt 2017}
program t_tand;

{$apptype console}

uses
  math;


{Vereinfachte Implementation einer tand-Funktion, d.h. Tangens mit }
{Argument in Grad. Achtung: nur bis ca 10^13 = 2^53/180 genau, weil}
{ich nicht noch mehr DAMath-Funktioen einbauen wollte. Compilierbar}
{ab Delphi 6}


{---------------------------------------------------------------------------}
function floord(x: double): double;
  {-Return the largest integer <= x}
var
  t: double;
begin
  t := int(x);
  if (x>=0.0) or (x=t) then floord := t
  else floord := t - 1.0;
end;


{---------------------------------------------------------------------------}
procedure trig_deg(x: double; var y,z: double; var n: integer; var m45: boolean);
  {-Reduce x in degrees mod 90; y=x mod 90, |y|<45. z=x/45, m45 if x is multiple of 45}
const
  c45: single = 45.0;
begin
  {Basic internal reduction routine mod 90. Use Cody/Waite logic, but no}
  {pseudo-multiprecision because 45.0 has only 6 non-zero mantissa bits.}
  if x=0.0 then begin
    y := 0.0;
    z := 0.0;
    n := 0;
    m45 := true;
  end
  else begin
    z := x/c45;
    m45 := (frac(z)=0.0) and (frac(x)=0.0);
    y := floord(z);
    n := trunc(y - 16.0*floord(y/16.0));
    if odd(n) then begin
      inc(n);
      y := y + 1.0;
    end;
    n := (n shr 1) and 7;
    y := x - y*c45;
  end;
end;


{---------------------------------------------------------------------------}
function tand(x: double): double;
  {-Return tan(x), x in degrees}
var
  y,z: double;
  n : integer;
  m45: boolean;
begin
  trig_deg(x,y,z,n,m45);
  if m45 then begin
    z := abs(z);
    y := sign(x);
    case round(4.0*frac(0.25*z)) of
        0: tand := 0.0;
        1: tand := y;
        2: tand := Infinity;
      else tand := -y;
    end;
  end
  else begin
    z := DegToRad(y);
    if odd(n) then tand := -cot(z)
    else tand := tan(z);
  end;
end;

var
  d: integer;
begin
  d := 0;
  while d<=360 do begin
    writeln(d:5, tand(d):25:16);
    d := d + 15;
  end;
end.
Die Ausgabe ist dann
Code:
    0       0.0000000000000000
   15       0.2679491924311228
   30       0.5773502691896258
   45       1.0000000000000000
   60       1.7320508075688770
   75       3.7320508075688768
   90                     +Inf
  105      -3.7320508075688768
  120      -1.7320508075688770
  135      -1.0000000000000000
  150      -0.5773502691896258
  165      -0.2679491924311228
  180       0.0000000000000000
  195       0.2679491924311228
  210       0.5773502691896258
  225       1.0000000000000000
  240       1.7320508075688770
  255       3.7320508075688768
  270                     +Inf
  285      -3.7320508075688768
  300      -1.7320508075688770
  315      -1.0000000000000000
  330      -0.5773502691896258
  345      -0.2679491924311228
  360       0.0000000000000000
Im getesteten Bereich -10^10 .. 10^10 ist die Genauigkeit ist die gleiche wie bei DAMath
Code:
Test DAMath V0.95 with MP_Arith V1.37.01 (31/32 bit)  (c) 2013-2017 W.Ehrhardt
Karatsuba cutoffs: mul/sqr = 16/32
Toom-3, BZ cutoffs: mul/sqr = 32/64, div = 32
Current mp_float default bit precision = 160, decimal precision = 48.2
Machine eps for double = 2.22044604925E-0016

-----------------------------------------------------------------
Test of DAMath.tand
at 10000 random values in [-10000000000.0000000000 .. 10000000000.0000000000]
RMS = 0.38, max rel = 1.25 eps at
 x(dbl) = -4.52875830026280448E+0009 = $C1F0DEF5E1C43473
 y(dbl) = -1.75054511612172736E+0000 = $BFFC023B987EA53D
 y(mpf) = -1.75054511612172687511491287528577674916550040814


-----------------------------------------------------------------
Test of tand (Delphi Praxis)
at 10000 random values in [-10000000000.0000000000 .. 10000000000.0000000000]
RMS = 0.38, max rel = 1.25 eps at
 x(dbl) = -4.52875830026280448E+0009 = $C1F0DEF5E1C43473
 y(dbl) = -1.75054511612172736E+0000 = $BFFC023B987EA53D
 y(mpf) = -1.75054511612172687511491287528577674916550040814

Geändert von gammatester (21. Nov 2017 um 11:20 Uhr) Grund: Minus bei cot
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe
Online

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.425 Beiträge
 
Delphi 12 Athens
 
#26

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

  Alt 21. Nov 2017, 10:53
Hier eine vereinfachte Implementation meiner DAMath-Funktion komplett mit Test-Programm
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.064 Beiträge
 
Delphi 12 Athens
 
#27

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

  Alt 21. Nov 2017, 11:33
Dankesehr für die interessanten Ansätze.
Also liege ich mit meinem Ansatz NaN o.ä. nicht ganz falsch.
Das muss man aber wohl immer je nach Aufgabe abwägen, für mal schnell hier und da tan() benutzen ist das OK für mich.

@gammatester
DAMath und ähnliche werde ich mir mal in einer ruhigen Minute genauer ansehen, da steckt ja eine ganze Menge Gehirnschmalz drin.
Tolle Libraries

Was mich da einbischen wundert ist das du direkt auf 0.0 vergleichst, z.B.
  if x=0.0 then begin Ich versuche so etwas eigentlich immer das mit Epsilon abzufangen, speziell dann wenn die Eingaben aus anderen Rechen-Ergebnissen kommen.
Wann ist das Epsilon nötig, und wann nicht ?
Vielleicht bin ich wieder zu übervorsichtig und kann etwas einsparen

P.S.: Noch eine kleine Frage an die Zahlentheoretiker hier:
Ist denn +Infinity richtig gewählt, die tan Funktion kann doch springen ?
Läuft die Näherung von links und rechts denn immer auf +Infinity raus ) ?


Rollo
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.070 Beiträge
 
Delphi 10.4 Sydney
 
#28

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

  Alt 21. Nov 2017, 11:59
P.S.: Noch eine kleine Frage an die Zahlentheoretiker hier:
Ist denn +Infinity richtig gewählt, die tan Funktion kann doch springen ?
Läuft die Näherung von links und rechts denn immer auf +Infinity raus ) ?
Wie ist denn der Tangens = tan(x) definiert?
In der Regel sagt man sin(x) / cos(x)!
Bei 90° haben wir sin(90°) / cos(90°) = 1 / 0.
Irgendetwas durch irgendetwas unendlich kleines ist halt abnormal übergroß bzw. unendlich.
Ob unendlich positiv oder negativ hängt von den Vorzeichen von Nenner und Zähler ab.
Vereinfacht: Positiv unendlich bei 90° und minus unendlich bei 270° (da 1 / 0 und -1 / 0).
  Mit Zitat antworten Zitat
gammatester

Registriert seit: 6. Dez 2005
999 Beiträge
 
#29

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

  Alt 21. Nov 2017, 12:16
Was mich da einbischen wundert ist das du direkt auf 0.0 vergleichst, z.B.
  if x=0.0 then begin Ich versuche so etwas eigentlich immer das mit Epsilon abzufangen, speziell dann wenn die Eingaben aus anderen Rechen-Ergebnissen kommen.
Wann ist das Epsilon nötig, und wann nicht ?
Vielleicht bin ich wieder zu übervorsichtig und kann etwas einsparen
MM ist diese epsilon Sache meist falsch bzw unnötig. Für kleine x ist tan(x) ~ x und 0 ist nun mal das einzige Vielfache von 45 nahe 0, also kein Problem.

Zu Epsilon bzw zur Math-Funktion iszero. Ebenson wie tan(x) ~ x gilt zB sinh(x) ~ x und arcsinh(x) ~ x, aber was liefert Delphi: Für Delphi 6 ist wenigsten der sinh-Wert korrekt
Code:
sinh(1e-20) = 1e-20
arcsinh(1e-20) = 0
Ab Delphi 7 siehts dann so aus
Code:
sinh(1e-20) = 0
arcsinh(1e-20) = 0
Ich benutzte diese epsilons, wenn ich zB vergleichen will ob kleine Änderungen vorliegen, d.h. ob x+delta ~ x ist, und dann als Test in der Form if abs(delta)<= epsilon*abs(x) .

Edit: Sinnvoll sind die Epsilons sind auch bei irrationalen Nullstellen von Funktionen, zB eben beim Tangens die Nullstellen pi/2 + n*Pi des Cosinus.

Geändert von gammatester (21. Nov 2017 um 12:24 Uhr)
  Mit Zitat antworten Zitat
Fritzew

Registriert seit: 18. Nov 2015
Ort: Kehl
678 Beiträge
 
Delphi 11 Alexandria
 
#30

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

  Alt 21. Nov 2017, 12:23
Eine sehr interessante Diskussion hier.
Habe gerade mal geschaut wie das bei uns gelöst ist.
Mal davon abgesehen das wir Winkelfunktionen so weit es geht vermeiden..

Wir haben einen Static Record Tdeg die bei uns für Ermittlung der Tan, etc zuständig ist.

Spasseshalber habe ich mal den Test erweitert

Tand: Gammatester
Tdeg: unsere Routine
Tan aus der system.math

Ergebnisse:
Code:
Tand :   0       0.0000000000000000
Tdeg :   0       0.0000000000000000
Tan :   0       0.0000000000000000

Tand :  15       0.2679491924311228
Tdeg :  15       0.2679491924311228
Tan :  15       0.2679491937160492

Tand :  30       0.5773502691896258
Tdeg :  30       0.5773502691896258
Tan :  30       0.5773502588272095

Tand :  45       1.0000000000000000
Tdeg :  45       1.0000000000000000
Tan :  45       1.0000000000000000

Tand :  60       1.7320508075688770
Tdeg :  60       1.7320508075688779
Tan :  60       1.7320508956909180

Tand :  75       3.7320508075688768
Tdeg :  75       3.7320508075688776
Tan :  75       3.7320504188537598

Tand :  90                     +Inf
Tdeg :  90                     +Inf
Tan :  90-22877332.0000000000000000

Tand : 105      -3.7320508075688768
Tdeg : 105      -3.7320508075688763
Tan : 105      -3.7320508956909180

Tand : 120      -1.7320508075688770
Tdeg : 120      -1.7320508075688763
Tan : 120      -1.7320505380630493

Tand : 135      -1.0000000000000000
Tdeg : 135      -1.0000000000000000
Tan : 135      -1.0000000000000000

Tand : 150      -0.5773502691896258
Tdeg : 150      -0.5773502691896256
Tan : 150      -0.5773503184318542

Tand : 165      -0.2679491924311228
Tdeg : 165      -0.2679491924311225
Tan : 165      -0.2679493129253387

Tand : 180       0.0000000000000000
Tdeg : 180       0.0000000000000000
Tan : 180       0.0000000874227766

Tand : 195       0.2679491924311228
Tdeg : 195       0.2679491924311228
Tan : 195       0.2679492235183716

Tand : 210       0.5773502691896258
Tdeg : 210       0.5773502691896260
Tan : 210       0.5773502588272095

Tand : 225       1.0000000000000000
Tdeg : 225       1.0000000000000000
Tan : 225       0.9999998807907104

Tand : 240       1.7320508075688770
Tdeg : 240       1.7320508075688790
Tan : 240       1.7320512533187866

Tand : 255       3.7320508075688768
Tdeg : 255       3.7320508075688794
Tan : 255       3.7320518493652344

Tand : 270                     +Inf
Tdeg : 270                     +Inf
Tan : 270-83858280.0000000000000000

Tand : 285      -3.7320508075688768
Tdeg : 285      -3.7320508075688719
Tan : 285      -3.7320513725280762

Tand : 300      -1.7320508075688770
Tdeg : 300      -1.7320508075688768
Tan : 300      -1.7320511341094971

Tand : 315      -1.0000000000000000
Tdeg : 315       1.0000000000000000
Tan : 315      -1.0000002384185791

Tand : 330      -0.5773502691896258
Tdeg : 330      -0.5773502691896253
Tan : 330      -0.5773505568504334

Tand : 345      -0.2679491924311228
Tdeg : 345      -0.2679491924311226
Tan : 345      -0.2679489552974701

Tand : 360       0.0000000000000000
Tdeg : 360       0.0000000000000000
Tan : 360       0.0000001748455531
Damit kann ich leben denke ich
Fritz Westermann
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 16:48 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