![]() |
AW: tan() von Single, Double, etc.
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. |
AW: tan() von Single, Double, etc.
Delphi-Quellcode:
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 |
AW: tan() von Single, Double, etc.
Zitat:
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; |
AW: tan() von Single, Double, etc.
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; |
AW: tan() von Single, Double, etc.
Zitat:
Delphi-Quellcode:
Die Ausgabe ist dann
{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.
Code:
Im getesteten Bereich -10^10 .. 10^10 ist die Genauigkeit ist die gleiche wie bei DAMath
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
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 |
AW: tan() von Single, Double, etc.
Zitat:
|
AW: tan() von Single, Double, etc.
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 :thumb: Was mich da einbischen wundert ist das du direkt auf 0.0 vergleichst, z.B.
Delphi-Quellcode:
Ich versuche so etwas eigentlich immer das mit Epsilon abzufangen, speziell dann wenn die Eingaben aus anderen Rechen-Ergebnissen kommen.
if x=0.0 then begin
Wann ist das Epsilon nötig, und wann nicht ? Vielleicht bin ich wieder zu übervorsichtig und kann etwas einsparen :stupid: P.S.: Noch eine kleine Frage an die Zahlentheoretiker hier: Ist denn +Infinity richtig gewählt, die tan Funktion kann ![]() Läuft die Näherung von links und rechts denn immer auf +Infinity raus ) ? Rollo |
AW: tan() von Single, Double, etc.
Zitat:
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). |
AW: tan() von Single, Double, etc.
Zitat:
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:
Ab Delphi 7 siehts dann so aus
sinh(1e-20) = 1e-20
arcsinh(1e-20) = 0
Code:
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
sinh(1e-20) = 0
arcsinh(1e-20) = 0
Delphi-Quellcode:
.
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. |
AW: tan() von Single, Double, etc.
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:
Damit kann ich leben denke ich :-)
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 |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:53 Uhr. |
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-2025 by Thomas Breitkreuz