GSL的数据类型之复数

    作为一个数值计算库,gsl主要关注浮点数的运算。且完全继承了c语言的浮点数体系,即分为单精度float型(32-bit),双精度double型(64-bit)以及扩展精度浮点数long double型(80-bit)。

    对应不同精度的需要,gsl中一些结构体、函数也有double,float与long double三套。其中,double型为这些结构体与函数的默认数据类型,而float与long double型则会在标示符中加_float或_long_double后缀予以强调(例如下文中复数结构体的实现)。

    当然,一个科学计算库对复数的支持是必不可少的。在gsl中,复数类型的声明在gsl_complex.h中,而复数相关的数学函数的声明则在gsl_complex_math.h中;编写数值计算程序时,千万别忘了调用这些头文件。

   gsl本身是使用c语言实现的,其中使用结构体来实现不同精度的复数类型。这种方式虽然不如基于c++面向对象的实现来得优雅,当然还是相当清晰的。其声明如下:

gsl_complex.h …… typedef struct { long double dat[2]; } gsl_complex_long_double; typedef struct { double dat[2]; } gsl_complex; typedef struct { float dat[2]; } gsl_complex_float; ……

 

以双精度复数gsl_complex为例,实际上它只是两个双精度数的简单封装。其中gsl_complex->dat[0]即实数部分,而gsl_complex->dat[1]即虚数部分。

这样就不难理解下面这组对复数进行操作的宏了:

gsl_complex.h …… #define GSL_REAL(z) ((z).dat[0]) //获得z的实数部分 #define GSL_IMAG(z) ((z).dat[1]) //获得z的虚数部分 #define GSL_COMPLEX_P(zp) ((zp)->dat) //获得复数结构体指针zp对应的复数数据(包括实数与虚数) …… #define GSL_SET_COMPLEX(zp,x,y) do {(zp)->dat[0]=(x); (zp)->dat[1]=(y);} while(0) //给复数结构体指针zp赋值,其中实数部分为x,虚数部分为y #define GSL_SET_REAL(zp,x) do {(zp)->dat[0]=(x);} while(0) #define GSL_SET_IMAG(zp,y) do {(zp)->dat[1]=(y);} while(0) ……

 

此外利用GSL_SET_COMPLEX宏,gsl还提供了两个创建复数结构体的函数:

math.c …… gsl_complex gsl_complex_rect (double x, double y) { gsl_complex z; GSL_SET_COMPLEX (&z, x, y); return z; } /* 返回复数结构体z = x + i y */ gsl_complex gsl_complex_polar (double r, double theta) { gsl_complex z; GSL_SET_COMPLEX (&z, r * cos (theta), r * sin (theta)); return z; } /* 返回复数结构体z = r*exp(i*theta) */ ……

 值得注意的是,考虑到科学计算中复数的使用频率很高,gsl_complex_rect还有一个内联函数版本:

#ifdef HAVE_INLINE extern inline gsl_complex gsl_complex_rect (double x, double y) { gsl_complex z; GSL_SET_COMPLEX (&z, x, y); return z; } #endif

这里的源码证实了上一篇提到的使用内联函数需要在编译参数中加上-D HAVE_INLINE这一事实。

 

gsl还提供了一系列与复数相关的函数, 读者可以在gsl手册http://www.gnu.org/software/gsl/manual/html_node/Complex-Numbe一节中找到相关介绍,当然也可以直接阅读源代码中math.c的complex部分。下面我们讨论一个例子,即用来求复数平方根的gsl_complex_sqrt函数:

math.c …… gsl_complex gsl_complex_sqrt (gsl_complex a) { /* z=sqrt(a) */ gsl_complex z; if (GSL_REAL (a) == 0.0 && GSL_IMAG (a) == 0.0) { GSL_SET_COMPLEX (&z, 0, 0); } else { double x = fabs (GSL_REAL (a)); double y = fabs (GSL_IMAG (a)); double w; if (x >= y) { double t = y / x; w = sqrt (x) * sqrt (0.5 * (1.0 + sqrt (1.0 + t * t))); } else { double t = x / y; w = sqrt (y) * sqrt (0.5 * (t + sqrt (1.0 + t * t))); } /*设z=w+iv,则由z^2=a可以得到方程组: w^2-v^2=x,2wv=y 解之,即得上面的式子。 */ if (GSL_REAL (a) >= 0.0) { double ai = GSL_IMAG (a); GSL_SET_COMPLEX (&z, w, ai / (2.0 * w)); } else { double ai = GSL_IMAG (a); double vi = (ai >= 0) ? w : -w; GSL_SET_COMPLEX (&z, ai / (2.0 * vi), vi); } } return z; } ……

      由于sqrt(a)是一个多值函数,按照gsl中的规定,函数体将返回sqrt(a)的主值。源代码中对GSL_REAL(a)的判断语句,就是用来确保z是sqrt(a)的主值的。

      gsl对x+iy型复数的支持还是很全面的。但是却没有提供ρe形式的复数结构体。有时间可以考虑改进一下:-D

      

 

 

你可能感兴趣的:(c,struct,扩展,语言,float,math.h)