diff options
author | jofret | 2008-05-20 07:26:09 +0000 |
---|---|---|
committer | jofret | 2008-05-20 07:26:09 +0000 |
commit | 54c5df264795ceb76bba56b03cafe01578452608 (patch) | |
tree | c9f57574b0b05c3ee02e153303aaf9f827e19c28 | |
parent | cd9b4f56d9cb58aff3511996dc517196c026863a (diff) | |
download | scilab2c-54c5df264795ceb76bba56b03cafe01578452608.tar.gz scilab2c-54c5df264795ceb76bba56b03cafe01578452608.tar.bz2 scilab2c-54c5df264795ceb76bba56b03cafe01578452608.zip |
* Importing Scilab ATAN algorithm
* License update
* Tests
-rw-r--r-- | src/elementaryFunctions/atan/Makefile.am | 9 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/Makefile.in | 13 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/catana.c | 20 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/catans.c | 297 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/datana.c | 20 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/datans.c | 20 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/satana.c | 20 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/testAtan.h | 32 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/testDoubleAtan.c | 56 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/testFloatAtan.c | 55 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/zatana.c | 20 | ||||
-rw-r--r-- | src/elementaryFunctions/atan/zatans.c | 290 |
12 files changed, 771 insertions, 81 deletions
diff --git a/src/elementaryFunctions/atan/Makefile.am b/src/elementaryFunctions/atan/Makefile.am index 07208ce6..8cbdbea8 100644 --- a/src/elementaryFunctions/atan/Makefile.am +++ b/src/elementaryFunctions/atan/Makefile.am @@ -10,8 +10,10 @@ ## ## -libAtan_la_CFLAGS = -I ../../type \ - -I ../includes +libAtan_la_CFLAGS = -I $(top_builddir)/type \ + -I $(top_builddir)/elementaryFunctions/includes \ + -I $(top_builddir)/auxiliaryFunctions/includes + instdir = $(top_builddir)/lib @@ -39,7 +41,10 @@ check_INCLUDES = -I $(top_builddir)/elementaryFunctions/includes \ check_LDADD = $(top_builddir)/type/libDoubleComplex.la \ $(top_builddir)/type/libFloatComplex.la \ + $(top_builddir)/lib/lapack/libscilapack.la \ + $(top_builddir)/elementaryFunctions/sqrt/libSqrt.la \ $(top_builddir)/elementaryFunctions/atan/libAtan.la \ + $(top_builddir)/auxiliaryFunctions/abs/libAbs.la \ @LIBMATH@ check_PROGRAMS = testFloatAtan testDoubleAtan diff --git a/src/elementaryFunctions/atan/Makefile.in b/src/elementaryFunctions/atan/Makefile.in index 07778fb6..272beaa8 100644 --- a/src/elementaryFunctions/atan/Makefile.in +++ b/src/elementaryFunctions/atan/Makefile.in @@ -66,7 +66,10 @@ am_testDoubleAtan_OBJECTS = testDoubleAtan-testDoubleAtan.$(OBJEXT) testDoubleAtan_OBJECTS = $(am_testDoubleAtan_OBJECTS) am__DEPENDENCIES_1 = $(top_builddir)/type/libDoubleComplex.la \ $(top_builddir)/type/libFloatComplex.la \ - $(top_builddir)/elementaryFunctions/atan/libAtan.la + $(top_builddir)/lib/lapack/libscilapack.la \ + $(top_builddir)/elementaryFunctions/sqrt/libSqrt.la \ + $(top_builddir)/elementaryFunctions/atan/libAtan.la \ + $(top_builddir)/auxiliaryFunctions/abs/libAbs.la testDoubleAtan_DEPENDENCIES = $(am__DEPENDENCIES_1) testDoubleAtan_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(testDoubleAtan_CFLAGS) \ @@ -206,8 +209,9 @@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -libAtan_la_CFLAGS = -I ../../type \ - -I ../includes +libAtan_la_CFLAGS = -I $(top_builddir)/type \ + -I $(top_builddir)/elementaryFunctions/includes \ + -I $(top_builddir)/auxiliaryFunctions/includes instdir = $(top_builddir)/lib pkglib_LTLIBRARIES = libAtan.la @@ -231,7 +235,10 @@ check_INCLUDES = -I $(top_builddir)/elementaryFunctions/includes \ check_LDADD = $(top_builddir)/type/libDoubleComplex.la \ $(top_builddir)/type/libFloatComplex.la \ + $(top_builddir)/lib/lapack/libscilapack.la \ + $(top_builddir)/elementaryFunctions/sqrt/libSqrt.la \ $(top_builddir)/elementaryFunctions/atan/libAtan.la \ + $(top_builddir)/auxiliaryFunctions/abs/libAbs.la \ @LIBMATH@ diff --git a/src/elementaryFunctions/atan/catana.c b/src/elementaryFunctions/atan/catana.c index 66d23e62..d6335cd8 100644 --- a/src/elementaryFunctions/atan/catana.c +++ b/src/elementaryFunctions/atan/catana.c @@ -1,14 +1,14 @@ /* -** -*- C -*- -** -** catana.c -** Made by Bruno JOFRET <bruno.jofret@inria.fr> -** -** Started on Thu Dec 7 14:54:24 2006 jofret -** Last update Mon Oct 22 09:57:09 2007 bruno -** -** Copyright INRIA 2006 -*/ + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2006-2008 - INRIA - Bruno JOFRET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt + * + */ #include "atan.h" diff --git a/src/elementaryFunctions/atan/catans.c b/src/elementaryFunctions/atan/catans.c index ba5fdc2f..ba448d96 100644 --- a/src/elementaryFunctions/atan/catans.c +++ b/src/elementaryFunctions/atan/catans.c @@ -10,9 +10,302 @@ * */ +/* + PURPOSE + watan compute the arctangent of a complex number + y = yr + i yi = atan(x), x = xr + i xi + + CALLING LIST / PARAMETERS + subroutine watan(xr,xi,yr,yi) + double precision xr,xi,yr,yi + + xr,xi: real and imaginary parts of the complex number + yr,yi: real and imaginary parts of the result + yr,yi may have the same memory cases than xr et xi + + COPYRIGHT (C) 2001 Bruno Pincon and Lydia van Dijk + Written by Bruno Pincon <Bruno.Pincon@iecn.u-nancy.fr> so + as to get more precision. Also to fix the + behavior at the singular points and at the branch cuts. + Polished by Lydia van Dijk + <lvandijk@hammersmith-consulting.com> + + CHANGES : - (Bruno on 2001 May 22) for ysptrk use a + minimax polynome to enlarge the special + evaluation zone |s| < SLIM. Also rename + this function as lnp1m1. + - (Bruno on 2001 June 7) better handling + of spurious over/underflow ; remove + the call to pythag ; better accuracy + in the real part for z near +-i + + EXTERNALS FUNCTIONS + dlamch + lnp1m1 (at the end of this file) + + ALGORITHM : noting z = a + i*b, we have: + Z = yr + yi*b = arctan(z) = (i/2) * log( (i+z)/(i-z) ) + + This function has two branch points at +i and -i and the + chosen branch cuts are the two half-straight lines + D1 = [i, i*oo) and D2 = (-i*oo, i]. The function is then + analytic in C \ (D1 U D2)). + + From the definition it follows that: + + yr = 0.5 Arg ( (i+z)/(i-z) ) (1) + yi = 0.5 log (|(i+z)/(i-z)|) (2) + + so lim (z -> +- i) yr = undefined (and Nan is logical) + lim (z -> +i) yi = +oo + lim (z -> -i) yi = -oo + + The real part of arctan(z) is discontinuous across D1 and D2 + and we impose the following definitions: + if imag(z) > 1 then + Arg(arctan(z)) = pi/2 (=lim real(z) -> 0+) + if imag(z) < 1 then + Arg(arctan(z)) = -pi/2 (=lim real(z) -> 0-) + + + Basic evaluation: if we write (i+z)/(i-z) using + z = a + i*b, we get: + + i+z 1-(a**2+b**2) + i*(2a) + --- = ---------------------- + i-z a**2 + (1-b)**2 + + then, with r2 = |z|^2 = a**2 + b**2 : + + yr = 0.5 * Arg(1-r2 + (2*a)*i) + = 0.5 * atan2(2a, (1-r2)) (3) + + This formula is changed when r2 > RMAX (max pos float) + and also when |1-r2| and |a| are near 0 (see comments + in the code). + + After some math: + + yi = 0.25 * log( (a**2 + (b + 1)**2) / + (a**2 + (b - 1)**2) ) (4) + + Evaluation for "big" |z| + ------------------------ + + If |z| is "big", the direct evaluation of yi by (4) may + suffer of innaccuracies and of spurious overflow. Noting + that s = 2 b / (1 + |z|**2), we have: + + yi = 0.25 log ( (1 + s)/(1 - s) ) (5) + + 3 5 + yi = 0.25*( 2 * ( s + 1/3 s + 1/5 s + ... )) + + yi = 0.25 * lnp1m1(s) if |s| < SLIM + + So if |s| is less than SLIM we switch to a special + evaluation done by the function lnp1m1. The + threshold value SLIM is choosen by experiment + (with the Pari-gp software). For |s| + "very small" we used a truncated taylor dvp, + else a minimax polynome (see lnp1m1). + + To avoid spurious overflows (which result in spurious + underflows for s) in computing s with s= 2 b / (1 + |z|**2) + when |z|^2 > RMAX (max positive float) we use : + + s = 2d0 / ( (a/b)*a + b ) + + but if |b| = Inf this formula leads to NaN when + |a| is also Inf. As we have : + + |s| <= 2 / |b| + + we impose simply : s = 0 when |b| = Inf + + Evaluation for z very near to i or -i: + -------------------------------------- + Floating point numbers of the form a+i or a-i with 0 < + a**2 < tiny (approximately 1d-308) may lead to underflow + (i.e., a**2 = 0) and the logarithm will break formula (4). + So we switch to the following formulas: + + If b = +-1 and |a| < sqrt(tiny) approximately 1d-150 (say) + then (by using that a**2 + 4 = 4 in machine for such a): + + yi = 0.5 * log( 2/|a| ) for b=1 + + yi = 0.5 * log( |a|/2 ) for b=-1 + + finally: yi = 0.5 * sign(b) * log( 2/|a| ) + yi = 0.5 * sign(b) * (log(2) - log(|a|)) (6) + + The last trick is to avoid overflow for |a|=tiny! In fact + this formula may be used until a**2 + 4 = 4 so that the + threshold value may be larger. +*/ + +#include <math.h> #include "atan.h" +#include "abs.h" +#include "lapack.h" + +#define _sign(a, b) b >=0 ? a : -a + +/* + PURPOSE : Compute v = log ( (1 + s)/(1 - s) ) + for small s, this is for |s| < SLIM = 0.20 + + ALGORITHM : + 1/ if |s| is "very small" we use a truncated + taylor dvp (by keeping 3 terms) from : + 2 4 6 + t = 2 * s * ( 1 + 1/3 s + 1/5 s + [ 1/7 s + ....] ) + 2 4 + t = 2 * s * ( 1 + 1/3 s + 1/5 s + er) + + The limit E until we use this formula may be simply + gotten so that the negliged part er is such that : + 2 4 + (#) er <= epsm * ( 1 + 1/3 s + 1/5 s ) for all |s|<= E + + As er = 1/7 s^6 + 1/9 s^8 + ... + er <= 1/7 * s^6 ( 1 + s^2 + s^4 + ...) = 1/7 s^6/(1-s^2) + + the inequality (#) is forced if : + + 1/7 s^6 / (1-s^2) <= epsm * ( 1 + 1/3 s^2 + 1/5 s^4 ) + + s^6 <= 7 epsm * (1 - 2/3 s^2 - 3/15 s^4 - 1/5 s^6) + + So that E is very near (7 epsm)^(1/6) (approximately 3.032d-3): + + 2/ For larger |s| we used a minimax polynome : + + yi = s * (2 + d3 s^3 + d5 s^5 .... + d13 s^13 + d15 s^15) + + This polynome was computed (by some remes algorithm) following + (*) the sin(x) example (p 39) of the book : + + "ELEMENTARY FUNCTIONS" + "Algorithms and implementation" + J.M. Muller (Birkhauser) + + (*) without the additionnal raffinement to get the first coefs + very near floating point numbers) +*/ +static float lnp1m1(float _dblVar) +{ + static float sdblD3 = 0.66666666666672679472f; + static float sdblD5 = 0.39999999996176889299f; + static float sdblD7 = 0.28571429392829380980f; + static float sdblD9 = 0.22222138684562683797f; + static float sdblD11 = 0.18186349187499222459f; + static float sdblD13 = 0.15250315884469364710f; + static float sdblD15 = 0.15367270224757008114f; + static float sdblE = 3.032E-3f; + static float sdblC3 = 2.0f/3.0f; + static float sdblC5 = 2.0f/5.0f; + + float dblS2 = _dblVar * _dblVar; + if( dabss(_dblVar) <= sdblE) + return _dblVar * (2 + dblS2 * (sdblC3 + sdblC5 * dblS2)); + else + return _dblVar * (2 + dblS2 * (sdblD3 + dblS2 * (sdblD5 + dblS2 * (sdblD7 + dblS2 * (sdblD9 + dblS2 * (sdblD11 + dblS2 * (sdblD13 + dblS2 * sdblD15))))))); +} + + floatComplex catans(floatComplex z) { - /* FIXME : Let's code... */ - return z; + static float sSlim = 0.2f; + /* . + ** / \ WARNING : this algorithm was based on double precision + ** / ! \ using float truncate the value to 0. + ** `----' + ** + ** static float sAlim = 1E-150f; + */ + static float sAlim = 0.0f; + static float sTol = 0.3f; + static float sLn2 = 0.6931471805599453094172321f; + + float RMax = (float) getOverflowThreshold(); + float Pi_2 = 2.0f * satans(1); + + float _inReal = creals(z); + float _inImg = cimags(z); + float _outReal = 0; + float _outImg = 0; + + /* Temporary variables */ + float R2 = 0; + float S = 0; + + + if(_inImg == 0) + { + _outReal = satans(_inReal); + _outImg = 0; + } + else + { + R2 = _inReal * _inReal + _inImg * _inImg; /* Oo */ + if(R2 > RMax) + { + if( dabss(_inImg) > RMax) + S = 0; + else + S = 1.0f / (((0.5f * _inReal) / _inImg) * _inReal + 0.5f * _inImg ); + } + else + S = (2 * _inImg) / (1+R2); + + if(dabss(S) < sSlim) + { + /* + s is small: |s| < SLIM <=> |z| outside the following disks: + D+ = D(center = [0; 1/slim], radius = sqrt(1/slim**2 - 1)) if b > 0 + D- = D(center = [0; -1/slim], radius = sqrt(1/slim**2 - 1)) if b < 0 + use the special evaluation of log((1+s)/(1-s)) (5) + */ + _outImg = lnp1m1(S) * 0.25f; + } + else + { + if(sabss(S) == 1 && sabss(_inReal) <= sAlim) + { + /* |s| >= SLIM => |z| is inside D+ or D- */ + _outImg = _sign(0.5f,_inImg) * ( sLn2 - logf(sabss(_inReal))); + } + else + { + _outImg = 0.25f * logf((powf(_inReal,2) + powf((_inImg + 1.0f),2)) / (powf(_inReal,2) + powf((_inImg - 1.0f),2))); + } + } + if(_inReal == 0) + {/* z is purely imaginary */ + if( dabss(_inImg) > 1) + {/* got sign(b) * pi/2 */ + _outReal = _sign(1, _inImg) * Pi_2; + } + else if( dabss(_inImg) == 1) + {/* got a Nan with 0/0 */ + _outReal = (_inReal - _inReal) / (_inReal - _inReal); /* Oo */ + } + else + _outReal = 0; + } + else if(R2 > RMax) + {/* _outImg is necessarily very near sign(a)* pi/2 */ + _outReal = _sign(1, _inReal) * Pi_2; + } + else if(sabss(1 - R2) + sabss(_inReal) <= sTol) + {/* |b| is very near 1 (and a is near 0) some cancellation occur in the (next) generic formula */ + _outReal = 0.5f * atan2f(2.0f * _inReal, (1.0f - _inImg) * (1.0f + _inImg) - powf(_inReal,2.0f)); + } + else + _outReal = 0.5f * atan2f(2.0f * _inReal, 1.0f - R2); + } + + return FloatComplex(_outReal, _outImg); } diff --git a/src/elementaryFunctions/atan/datana.c b/src/elementaryFunctions/atan/datana.c index 28a008ac..9b1d9c94 100644 --- a/src/elementaryFunctions/atan/datana.c +++ b/src/elementaryFunctions/atan/datana.c @@ -1,14 +1,14 @@ /* -** -*- C -*- -** -** datana.c -** Made by Bruno JOFRET <bruno.jofret@inria.fr> -** -** Started on Thu Dec 7 14:54:56 2006 jofret -** Last update Mon Oct 22 09:56:59 2007 bruno -** -** Copyright INRIA 2006 -*/ + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2006-2008 - INRIA - Bruno JOFRET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt + * + */ #include "atan.h" diff --git a/src/elementaryFunctions/atan/datans.c b/src/elementaryFunctions/atan/datans.c index c75e3625..a16df82d 100644 --- a/src/elementaryFunctions/atan/datans.c +++ b/src/elementaryFunctions/atan/datans.c @@ -1,14 +1,14 @@ /* -** -*- C -*- -** -** datans.c -** Made by Bruno JOFRET <bruno.jofret@inria.fr> -** -** Started on Thu Dec 7 12:02:41 2006 jofret -** Last update Thu Sep 6 11:53:57 2007 bruno -** -** Copyright INRIA 2006 -*/ + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2006-2008 - INRIA - Bruno JOFRET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt + * + */ #include <math.h> #include "atan.h" diff --git a/src/elementaryFunctions/atan/satana.c b/src/elementaryFunctions/atan/satana.c index 9e259f2f..639c6f58 100644 --- a/src/elementaryFunctions/atan/satana.c +++ b/src/elementaryFunctions/atan/satana.c @@ -1,14 +1,14 @@ /* -** -*- C -*- -** -** satana.c -** Made by Bruno JOFRET <bruno.jofret@inria.fr> -** -** Started on Thu Dec 7 16:03:27 2006 jofret -** Last update Mon Oct 22 09:56:10 2007 bruno -** -** Copyright INRIA 2006 -*/ + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2006-2008 - INRIA - Bruno JOFRET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt + * + */ #include "atan.h" diff --git a/src/elementaryFunctions/atan/testAtan.h b/src/elementaryFunctions/atan/testAtan.h index e3ba1569..90b9b1de 100644 --- a/src/elementaryFunctions/atan/testAtan.h +++ b/src/elementaryFunctions/atan/testAtan.h @@ -1,14 +1,14 @@ /* -** -*- C -*- -** -** testAtan.h -** Made by Bruno JOFRET <bruno.jofret@inria.fr> -** -** Started on Fri Mar 30 11:22:40 2007 jofret -** Last update Thu Sep 6 17:13:18 2007 bruno -** -** Copyright INRIA 2007 -*/ + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2007-2008 - INRIA - Bruno JOFRET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt + * + */ #ifndef _TESTATAN_H_ #define _TESTATAN_H_ @@ -20,8 +20,20 @@ void satansTest(void); +void satanaTest(void); + +void catansTest(void); + +void catanaTest(void); + void datansTest(void); +void datanaTest(void); + +void zatansTest(void); + +void zatanaTest(void); + int testAtan(void); #endif /* !_TESTATAN_H_ */ diff --git a/src/elementaryFunctions/atan/testDoubleAtan.c b/src/elementaryFunctions/atan/testDoubleAtan.c index a22bbed5..bc31f382 100644 --- a/src/elementaryFunctions/atan/testDoubleAtan.c +++ b/src/elementaryFunctions/atan/testDoubleAtan.c @@ -27,15 +27,59 @@ void datansTest() { printf("datans(-PI/6) = %e\n", datans(-DPI/6)); } +void zatansTest() { + doubleComplex pi_pi = DoubleComplex(DPI, DPI); + doubleComplex pi_2_pi_2 = DoubleComplex(DPI/2, DPI/2); + doubleComplex pi_2_pi_3 = DoubleComplex(DPI/2, DPI/3); + doubleComplex pi_2_pi_4 = DoubleComplex(DPI/2, DPI/4); + doubleComplex out; + + printf(">> Double Complex scalar\n"); + out = zatans(pi_pi); + printf("zatans(PI + I*PI) = %e + I * %e\n", zreals(out), zimags(out)); + out = zatans(pi_2_pi_2); + printf("zatans(PI/2 + I*PI/2) = %e + I * %e\n", zreals(out), zimags(out)); + out = zatans(pi_2_pi_3); + printf("zatans(PI/2 + I*PI/3) = %e + I * %e\n", zreals(out), zimags(out)); + out = zatans(pi_2_pi_4); + printf("zatans(PI/2 + I*PI/4) = %e + I * %e\n", zreals(out), zimags(out)); +} + +void datanaTest(void) { + double out[5]; + double in[5] = {DPI, DPI/2, DPI/3, DPI/4, DPI/6}; + int i = 0; + + printf(">> Double Array\n"); + datana(in, 5, out); + for (i = 0 ; i < 5 ; ++i) + printf("satana(array) = %f\n", out[i]); + +} + +void zatanaTest(void) { + doubleComplex in[4]; + doubleComplex out[4]; + int i = 0; + + in[0] = DoubleComplex(DPI, DPI); + in[1] = DoubleComplex(DPI/2, DPI/2); + in[2] = DoubleComplex(DPI/2, DPI/3); + in[3] = DoubleComplex(DPI/2, DPI/4); + + zatana(in, 4, out); + printf(">> Double Complex Array\n"); + for (i = 0 ; i < 4 ; ++i) + printf("zatana(array) = %e + I * %e\n", zreals(out[i]), zimags(out[i])); +} + int testAtan() { printf("\n>>>> Double Arc Tangeant Tests\n"); datansTest(); - /* FIXME : Implement some test here ... */ - /* - zatansTest(); - datanaTest(); - zatanaTest(); - */ + zatansTest(); + datanaTest(); + zatanaTest(); + return 0; } diff --git a/src/elementaryFunctions/atan/testFloatAtan.c b/src/elementaryFunctions/atan/testFloatAtan.c index b6724491..da757254 100644 --- a/src/elementaryFunctions/atan/testFloatAtan.c +++ b/src/elementaryFunctions/atan/testFloatAtan.c @@ -27,15 +27,58 @@ void satansTest() { printf("satans(-PI/6) = %f\n", satans(-FPI/6)); } +void catansTest(void) { + floatComplex pi_pi = FloatComplex(FPI, FPI); + floatComplex pi_2_pi_2 = FloatComplex(FPI/2, FPI/2); + floatComplex pi_2_pi_3 = FloatComplex(FPI/2, FPI/3); + floatComplex pi_2_pi_4 = FloatComplex(FPI/2, FPI/4); + floatComplex out; + + printf(">> Float Complex scalar\n"); + out = catans(pi_pi); + printf("catans(PI + I*PI) = %e + I * %e\n", creals(out), cimags(out)); + out = catans(pi_2_pi_2); + printf("catans(PI/2 + I*PI/2) = %e + I * %e\n", creals(out), cimags(out)); + out = catans(pi_2_pi_3); + printf("catans(PI/2 + I*PI/3) = %e + I * %e\n", creals(out), cimags(out)); + out = catans(pi_2_pi_4); + printf("catans(PI/2 + I*PI/4) = %e + I * %e\n", creals(out), cimags(out)); +} + +void satanaTest(void) { + float out[5]; + float in[5] = {FPI, FPI/2, FPI/3, FPI/4, FPI/6}; + int i = 0; + + printf(">> Float Array\n"); + satana(in, 5, out); + for (i = 0 ; i < 5 ; ++i) + printf("satana(array) = %f\n", out[i]); + +} + +void catanaTest(void) { + floatComplex in[4]; + floatComplex out[4]; + int i = 0; + + in[0] = FloatComplex(FPI, FPI); + in[1] = FloatComplex(FPI/2, FPI/2); + in[2] = FloatComplex(FPI/2, FPI/3); + in[3] = FloatComplex(FPI/2, FPI/4); + + catana(in, 4, out); + printf(">> Float Complex Array\n"); + for (i = 0 ; i < 4 ; ++i) + printf("catana(array) = %e + I * %e\n", creals(out[i]), cimags(out[i])); +} int testAtan() { printf("\n>>>> Float Arc tangeant Tests\n"); satansTest(); - /* FIXME : Implement some test here ... */ - /* - catansTest(); - satanaTest(); - catanaTest(); - */ + catansTest(); + satanaTest(); + catanaTest(); + return 0; } diff --git a/src/elementaryFunctions/atan/zatana.c b/src/elementaryFunctions/atan/zatana.c index 476d4662..bfe4fc34 100644 --- a/src/elementaryFunctions/atan/zatana.c +++ b/src/elementaryFunctions/atan/zatana.c @@ -1,14 +1,14 @@ /* -** -*- C -*- -** -** zatana.c -** Made by Bruno JOFRET <bruno.jofret@inria.fr> -** -** Started on Thu Dec 7 16:12:02 2006 jofret -** Last update Mon Oct 22 09:56:00 2007 bruno -** -** Copyright INRIA 2006 -*/ + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2006-2008 - INRIA - Bruno JOFRET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt + * + */ #include "atan.h" diff --git a/src/elementaryFunctions/atan/zatans.c b/src/elementaryFunctions/atan/zatans.c index 5dc471da..0c8ed72e 100644 --- a/src/elementaryFunctions/atan/zatans.c +++ b/src/elementaryFunctions/atan/zatans.c @@ -10,9 +10,295 @@ * */ +/* + PURPOSE + watan compute the arctangent of a complex number + y = yr + i yi = atan(x), x = xr + i xi + + CALLING LIST / PARAMETERS + subroutine watan(xr,xi,yr,yi) + double precision xr,xi,yr,yi + + xr,xi: real and imaginary parts of the complex number + yr,yi: real and imaginary parts of the result + yr,yi may have the same memory cases than xr et xi + + COPYRIGHT (C) 2001 Bruno Pincon and Lydia van Dijk + Written by Bruno Pincon <Bruno.Pincon@iecn.u-nancy.fr> so + as to get more precision. Also to fix the + behavior at the singular points and at the branch cuts. + Polished by Lydia van Dijk + <lvandijk@hammersmith-consulting.com> + + CHANGES : - (Bruno on 2001 May 22) for ysptrk use a + minimax polynome to enlarge the special + evaluation zone |s| < SLIM. Also rename + this function as lnp1m1. + - (Bruno on 2001 June 7) better handling + of spurious over/underflow ; remove + the call to pythag ; better accuracy + in the real part for z near +-i + + EXTERNALS FUNCTIONS + dlamch + lnp1m1 (at the end of this file) + + ALGORITHM : noting z = a + i*b, we have: + Z = yr + yi*b = arctan(z) = (i/2) * log( (i+z)/(i-z) ) + + This function has two branch points at +i and -i and the + chosen branch cuts are the two half-straight lines + D1 = [i, i*oo) and D2 = (-i*oo, i]. The function is then + analytic in C \ (D1 U D2)). + + From the definition it follows that: + + yr = 0.5 Arg ( (i+z)/(i-z) ) (1) + yi = 0.5 log (|(i+z)/(i-z)|) (2) + + so lim (z -> +- i) yr = undefined (and Nan is logical) + lim (z -> +i) yi = +oo + lim (z -> -i) yi = -oo + + The real part of arctan(z) is discontinuous across D1 and D2 + and we impose the following definitions: + if imag(z) > 1 then + Arg(arctan(z)) = pi/2 (=lim real(z) -> 0+) + if imag(z) < 1 then + Arg(arctan(z)) = -pi/2 (=lim real(z) -> 0-) + + + Basic evaluation: if we write (i+z)/(i-z) using + z = a + i*b, we get: + + i+z 1-(a**2+b**2) + i*(2a) + --- = ---------------------- + i-z a**2 + (1-b)**2 + + then, with r2 = |z|^2 = a**2 + b**2 : + + yr = 0.5 * Arg(1-r2 + (2*a)*i) + = 0.5 * atan2(2a, (1-r2)) (3) + + This formula is changed when r2 > RMAX (max pos float) + and also when |1-r2| and |a| are near 0 (see comments + in the code). + + After some math: + + yi = 0.25 * log( (a**2 + (b + 1)**2) / + (a**2 + (b - 1)**2) ) (4) + + Evaluation for "big" |z| + ------------------------ + + If |z| is "big", the direct evaluation of yi by (4) may + suffer of innaccuracies and of spurious overflow. Noting + that s = 2 b / (1 + |z|**2), we have: + + yi = 0.25 log ( (1 + s)/(1 - s) ) (5) + + 3 5 + yi = 0.25*( 2 * ( s + 1/3 s + 1/5 s + ... )) + + yi = 0.25 * lnp1m1(s) if |s| < SLIM + + So if |s| is less than SLIM we switch to a special + evaluation done by the function lnp1m1. The + threshold value SLIM is choosen by experiment + (with the Pari-gp software). For |s| + "very small" we used a truncated taylor dvp, + else a minimax polynome (see lnp1m1). + + To avoid spurious overflows (which result in spurious + underflows for s) in computing s with s= 2 b / (1 + |z|**2) + when |z|^2 > RMAX (max positive float) we use : + + s = 2d0 / ( (a/b)*a + b ) + + but if |b| = Inf this formula leads to NaN when + |a| is also Inf. As we have : + + |s| <= 2 / |b| + + we impose simply : s = 0 when |b| = Inf + + Evaluation for z very near to i or -i: + -------------------------------------- + Floating point numbers of the form a+i or a-i with 0 < + a**2 < tiny (approximately 1d-308) may lead to underflow + (i.e., a**2 = 0) and the logarithm will break formula (4). + So we switch to the following formulas: + + If b = +-1 and |a| < sqrt(tiny) approximately 1d-150 (say) + then (by using that a**2 + 4 = 4 in machine for such a): + + yi = 0.5 * log( 2/|a| ) for b=1 + + yi = 0.5 * log( |a|/2 ) for b=-1 + + finally: yi = 0.5 * sign(b) * log( 2/|a| ) + yi = 0.5 * sign(b) * (log(2) - log(|a|)) (6) + + The last trick is to avoid overflow for |a|=tiny! In fact + this formula may be used until a**2 + 4 = 4 so that the + threshold value may be larger. +*/ + +#include <math.h> #include "atan.h" +#include "abs.h" +#include "lapack.h" + +#define _sign(a, b) b >=0 ? a : -a + +/* + PURPOSE : Compute v = log ( (1 + s)/(1 - s) ) + for small s, this is for |s| < SLIM = 0.20 + + ALGORITHM : + 1/ if |s| is "very small" we use a truncated + taylor dvp (by keeping 3 terms) from : + 2 4 6 + t = 2 * s * ( 1 + 1/3 s + 1/5 s + [ 1/7 s + ....] ) + 2 4 + t = 2 * s * ( 1 + 1/3 s + 1/5 s + er) + + The limit E until we use this formula may be simply + gotten so that the negliged part er is such that : + 2 4 + (#) er <= epsm * ( 1 + 1/3 s + 1/5 s ) for all |s|<= E + + As er = 1/7 s^6 + 1/9 s^8 + ... + er <= 1/7 * s^6 ( 1 + s^2 + s^4 + ...) = 1/7 s^6/(1-s^2) + + the inequality (#) is forced if : + + 1/7 s^6 / (1-s^2) <= epsm * ( 1 + 1/3 s^2 + 1/5 s^4 ) + + s^6 <= 7 epsm * (1 - 2/3 s^2 - 3/15 s^4 - 1/5 s^6) + + So that E is very near (7 epsm)^(1/6) (approximately 3.032d-3): + + 2/ For larger |s| we used a minimax polynome : + + yi = s * (2 + d3 s^3 + d5 s^5 .... + d13 s^13 + d15 s^15) + + This polynome was computed (by some remes algorithm) following + (*) the sin(x) example (p 39) of the book : + + "ELEMENTARY FUNCTIONS" + "Algorithms and implementation" + J.M. Muller (Birkhauser) + + (*) without the additionnal raffinement to get the first coefs + very near floating point numbers) +*/ +static double lnp1m1(double _dblVar) +{ + static double sdblD3 = 0.66666666666672679472; + static double sdblD5 = 0.39999999996176889299; + static double sdblD7 = 0.28571429392829380980; + static double sdblD9 = 0.22222138684562683797; + static double sdblD11 = 0.18186349187499222459; + static double sdblD13 = 0.15250315884469364710; + static double sdblD15 = 0.15367270224757008114; + static double sdblE = 3.032E-3; + static double sdblC3 = 2.0/3.0; + static double sdblC5 = 2.0/5.0; + + double dblS2 = _dblVar * _dblVar; + if( dabss(_dblVar) <= sdblE) + return _dblVar * (2 + dblS2 * (sdblC3 + sdblC5 * dblS2)); + else + return _dblVar * (2 + dblS2 * (sdblD3 + dblS2 * (sdblD5 + dblS2 * (sdblD7 + dblS2 * (sdblD9 + dblS2 * (sdblD11 + dblS2 * (sdblD13 + dblS2 * sdblD15))))))); +} + + doubleComplex zatans(doubleComplex z) { - /* FIXME: Dummy... */ - return z; + static double sSlim = 0.2; + static double sAlim = 1E-150; + static double sTol = 0.3; + static double sLn2 = 0.6931471805599453094172321; + + double RMax = getOverflowThreshold(); + double Pi_2 = 2.0 * datans(1); + + double _inReal = zreals(z); + double _inImg = zimags(z); + double _outReal = 0; + double _outImg = 0; + + /* Temporary variables */ + double R2 = 0; + double S = 0; + + + if(_inImg == 0) + { + _outReal = datans(_inReal); + _outImg = 0; + } + else + { + R2 = _inReal * _inReal + _inImg * _inImg; /* Oo */ + if(R2 > RMax) + { + if( dabss(_inImg) > RMax) + S = 0; + else + S = 1 / (((0.5 * _inReal) / _inImg) * _inReal + 0.5 * _inImg ); + } + else + S = (2 * _inImg) / (1+R2); + + if(dabss(S) < sSlim) + { + /* + s is small: |s| < SLIM <=> |z| outside the following disks: + D+ = D(center = [0; 1/slim], radius = sqrt(1/slim**2 - 1)) if b > 0 + D- = D(center = [0; -1/slim], radius = sqrt(1/slim**2 - 1)) if b < 0 + use the special evaluation of log((1+s)/(1-s)) (5) + */ + _outImg = lnp1m1(S) * 0.25; + } + else + { + if(dabss(S) == 1 && dabss(_inReal) <= sAlim) + { + /* |s| >= SLIM => |z| is inside D+ or D- */ + _outImg = _sign(0.5,_inImg) * ( sLn2 - log(dabss(_inReal))); + } + else + { + _outImg = 0.25 * log((pow(_inReal,2) + pow((_inImg + 1),2)) / (pow(_inReal,2) + pow((_inImg - 1),2))); + } + } + if(_inReal == 0) + {/* z is purely imaginary */ + if( dabss(_inImg) > 1) + {/* got sign(b) * pi/2 */ + _outReal = _sign(1, _inImg) * Pi_2; + } + else if( dabss(_inImg) == 1) + {/* got a Nan with 0/0 */ + _outReal = (_inReal - _inReal) / (_inReal - _inReal); /* Oo */ + } + else + _outReal = 0; + } + else if(R2 > RMax) + {/* _outImg is necessarily very near sign(a)* pi/2 */ + _outReal = _sign(1, _inReal) * Pi_2; + } + else if(dabss(1 - R2) + dabss(_inReal) <= sTol) + {/* |b| is very near 1 (and a is near 0) some cancellation occur in the (next) generic formula */ + _outReal = 0.5 * atan2(2 * _inReal, (1-_inImg) * (1 + _inImg) - pow(_inReal,2)); + } + else + _outReal = 0.5 * atan2(2 * _inReal, 1 - R2); + } + + return DoubleComplex(_outReal, _outImg); } |