Go to the documentation of this file.00001 #include "f2c.h"
00002
00003 #ifdef KR_headers
00004 double log(), f__cabs(), atan2();
00005 #define ANSI(x) ()
00006 #else
00007 #define ANSI(x) x
00008 #undef abs
00009 #include "math.h"
00010 #ifdef __cplusplus
00011 extern "C" {
00012 #endif
00013 extern double f__cabs(double, double);
00014 #endif
00015
00016 #ifndef NO_DOUBLE_EXTENDED
00017 #ifndef GCC_COMPARE_BUG_FIXED
00018 #ifndef Pre20000310
00019 #ifdef Comment
00020 Some versions of gcc, such as 2.95.3 and 3.0.4, are buggy under -O2 or -O3:
00021 on IA32 (Intel 80x87) systems, they may do comparisons on values computed
00022 in extended-precision registers. This can lead to the test "s > s0" that
00023 was used below being carried out incorrectly. The fix below cannot be
00024 spoiled by overzealous optimization, since the compiler cannot know
00025 whether gcc_bug_bypass_diff_F2C will be nonzero. (We expect it always
00026 to be zero. The weird name is unlikely to collide with anything.)
00027
00028 An example (provided by Ulrich Jakobus) where the bug fix matters is
00029
00030 double complex a, b
00031 a = (.1099557428756427618354862829619, .9857360542953131909982289471372)
00032 b = log(a)
00033
00034 An alternative to the fix below would be to use 53-bit rounding precision,
00035 but the means of specifying this 80x87 feature are highly unportable.
00036 #endif
00037 #define BYPASS_GCC_COMPARE_BUG
00038 double (*gcc_bug_bypass_diff_F2C) ANSI((double*,double*));
00039 static double
00040 #ifdef KR_headers
00041 diff1(a,b) double *a, *b;
00042 #else
00043 diff1(double *a, double *b)
00044 #endif
00045 { return *a - *b; }
00046 #endif
00047 #endif
00048 #endif
00049
00050 #ifdef KR_headers
00051 VOID z_log(r, z) doublecomplex *r, *z;
00052 #else
00053 void z_log(doublecomplex *r, doublecomplex *z)
00054 #endif
00055 {
00056 double s, s0, t, t2, u, v;
00057 double zi = z->i, zr = z->r;
00058 #ifdef BYPASS_GCC_COMPARE_BUG
00059 double (*diff) ANSI((double*,double*));
00060 #endif
00061
00062 r->i = atan2(zi, zr);
00063 #ifdef Pre20000310
00064 r->r = log( f__cabs( zr, zi ) );
00065 #else
00066 if (zi < 0)
00067 zi = -zi;
00068 if (zr < 0)
00069 zr = -zr;
00070 if (zr < zi) {
00071 t = zi;
00072 zi = zr;
00073 zr = t;
00074 }
00075 t = zi/zr;
00076 s = zr * sqrt(1 + t*t);
00077
00078 if ((t = s - 1) < 0)
00079 t = -t;
00080 if (t > .01)
00081 r->r = log(s);
00082 else {
00083
00084 #ifdef Comment
00085
00086 log(1+x) = x - x^2/2 + x^3/3 - x^4/4 + - ...
00087
00088 = x(1 - x/2 + x^2/3 -+...)
00089
00090 [sqrt(y^2 + z^2) - 1] * [sqrt(y^2 + z^2) + 1] = y^2 + z^2 - 1, so
00091
00092 sqrt(y^2 + z^2) - 1 = (y^2 + z^2 - 1) / [sqrt(y^2 + z^2) + 1]
00093
00094 #endif
00095
00096 #ifdef BYPASS_GCC_COMPARE_BUG
00097 if (!(diff = gcc_bug_bypass_diff_F2C))
00098 diff = diff1;
00099 #endif
00100 t = ((zr*zr - 1.) + zi*zi) / (s + 1);
00101 t2 = t*t;
00102 s = 1. - 0.5*t;
00103 u = v = 1;
00104 do {
00105 s0 = s;
00106 u *= t2;
00107 v += 2;
00108 s += u/v - t*u/(v+1);
00109 }
00110 #ifdef BYPASS_GCC_COMPARE_BUG
00111 while(s - s0 > 1e-18 || (*diff)(&s,&s0) > 0.);
00112 #else
00113 while(s > s0);
00114 #endif
00115 r->r = s*t;
00116 }
00117 #endif
00118 }
00119 #ifdef __cplusplus
00120 }
00121 #endif