表格中的三角函数,在Python里都是属于math模块的。在之前 Python相关的数学运算函数 的文章里,已经介绍过math模块,该模块对应的C源代码位于Modules/mathmodule.c文件中...

    页面导航: 英文教程的下载地址:

    本篇文章是根据英文教程《Python Tutorial》来写的学习笔记。该英文教程的下载地址如下:

    百度盘地址:http://pan.baidu.com/s/1c0eXSQG

    DropBox地址:点此进入DropBox链接

    Google Drive:点此进入Google Drive链接

    这是学习笔记,不是翻译,因此,内容上会与英文原著有些不同。以下记录和上一篇文章一样,都是根据英文教程的第八章来写的。(文章中的部分链接,可能需要通过代理访问!)

    本篇文章也会涉及到Python的C源代码,这些C源码都是2.7.8版本的。想使用gdb来调试python源代码的话,就需要按照前面"Python基本的操作运算符"文章中所说的,使用configure --with-pydebug命令来重新编译安装python。

概述:

    Python中包含了一些和三角计算相关的函数,如下表所示:

函数名 描述
acos(x) 返回参数x的反余弦值,返回值以弧度为单位。
asin(x) 返回参数x的反正弦值,返回值以弧度为单位。
atan(x) 返回参数x的反正切值,返回值以弧度为单位。
atan2(y, x) 计算并返回atan(y / x)的值,返回值以弧度为单位。
cos(x) 返回角度x的余弦值(角度x以弧度为单位)。
hypot(x, y) 返回以x,y为两直角边的直接三角形的斜边长,
相当于sqrt(x*x + y*y),即x的平方加上y的平方,相加的
结果再开平方根。
sin(x) 返回角度x的正弦值(角度x以弧度为单位)。
tan(x) 返回角度x的正切值(角度x以弧度为单位)。
degrees(x) 将参数x由弧度转为度。
radians(x) 将参数x由度转为弧度。

    有关sin,cos这些三角函数的数学概念请参考 三角函数_维基百科 该链接对应的文章,有关asin,acos这些反三角函数的数学概念请参考 反三角函数_维基百科 该链接对应的文章。另外,有关三角学的数学概念,则请参考 三角学_维基百科 该链接对应的文章。

math模块里的三角函数:

    上面表格中的三角函数,在Python里都是属于math模块的。在之前"Python相关的数学运算函数"的文章里,已经介绍过math模块,该模块对应的C源代码位于Modules/mathmodule.c文件中:

[email protected]:~/Downloads/Python-2.7.8$ ls Modules/mathmodule.c 
Modules/mathmodule.c
[email protected]:~/Downloads/Python-2.7.8$ 


    该C文件里的math_methods数组中,就定义了math模块所包含的python函数,以及这些python函数在执行时,会调用的底层C函数:

static PyMethodDef math_methods[] = {
    {"acos",            math_acos,      METH_O,         math_acos_doc},
    {"acosh",           math_acosh,     METH_O,         math_acosh_doc},
    {"asin",            math_asin,      METH_O,         math_asin_doc},
    {"asinh",           math_asinh,     METH_O,         math_asinh_doc},
    {"atan",            math_atan,      METH_O,         math_atan_doc},
    {"atan2",           math_atan2,     METH_VARARGS,   math_atan2_doc},
    {"atanh",           math_atanh,     METH_O,         math_atanh_doc},
    {"ceil",            math_ceil,      METH_O,         math_ceil_doc},
    {"copysign",        math_copysign,  METH_VARARGS,   math_copysign_doc},
    {"cos",             math_cos,       METH_O,         math_cos_doc},
    {"cosh",            math_cosh,      METH_O,         math_cosh_doc},
    {"degrees",         math_degrees,   METH_O,         math_degrees_doc},
    {"erf",             math_erf,       METH_O,         math_erf_doc},
    {"erfc",            math_erfc,      METH_O,         math_erfc_doc},
    {"exp",             math_exp,       METH_O,         math_exp_doc},
    {"expm1",           math_expm1,     METH_O,         math_expm1_doc},
    {"fabs",            math_fabs,      METH_O,         math_fabs_doc},
    {"factorial",       math_factorial, METH_O,         math_factorial_doc},
    {"floor",           math_floor,     METH_O,         math_floor_doc},
    {"fmod",            math_fmod,      METH_VARARGS,   math_fmod_doc},
    {"frexp",           math_frexp,     METH_O,         math_frexp_doc},
    {"fsum",            math_fsum,      METH_O,         math_fsum_doc},
    {"gamma",           math_gamma,     METH_O,         math_gamma_doc},
    {"hypot",           math_hypot,     METH_VARARGS,   math_hypot_doc},
    {"isinf",           math_isinf,     METH_O,         math_isinf_doc},
    {"isnan",           math_isnan,     METH_O,         math_isnan_doc},
    {"ldexp",           math_ldexp,     METH_VARARGS,   math_ldexp_doc},
    {"lgamma",          math_lgamma,    METH_O,         math_lgamma_doc},
    {"log",             math_log,       METH_VARARGS,   math_log_doc},
    {"log1p",           math_log1p,     METH_O,         math_log1p_doc},
    {"log10",           math_log10,     METH_O,         math_log10_doc},
    {"modf",            math_modf,      METH_O,         math_modf_doc},
    {"pow",             math_pow,       METH_VARARGS,   math_pow_doc},
    {"radians",         math_radians,   METH_O,         math_radians_doc},
    {"sin",             math_sin,       METH_O,         math_sin_doc},
    {"sinh",            math_sinh,      METH_O,         math_sinh_doc},
    {"sqrt",            math_sqrt,      METH_O,         math_sqrt_doc},
    {"tan",             math_tan,       METH_O,         math_tan_doc},
    {"tanh",            math_tanh,      METH_O,         math_tanh_doc},
    {"trunc",           math_trunc,     METH_O,         math_trunc_doc},
    {NULL,              NULL}           /* sentinel */
};


    从上面的数组中可以看到,acos方法在Python脚本里执行时,会调用math_acos这个底层C函数去完成具体的操作:

[email protected]:~$ gdb -q python
Reading symbols from /mnt/zenglOX/Python-2.7.8/python...done.
(gdb) r
Starting program: /mnt/zenglOX/Python-2.7.8/python 
[Thread debugging using libthread_db enabled]
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
[41074 refs]
>>> math.acos(0.6)
Program received signal SIGINT, Interrupt. (按ctrl+c组合键来中断Python,并进入gdb调试界面)
0xb7ee09f8 in ___newselect_nocancel () from /lib/libc.so.6
(gdb) b math_acos
Breakpoint 1 at 0xb7fd3e35: file /mnt/zenglOX/Python-2.7.8/Modules/mathmodule.c, line 809.
(gdb) c
Continuing.
(此处再按一次回车符,让上面的math.acos(0.6)脚本得以执行,
并触发mathmodule.c里设置的断点)

Breakpoint 1, math_acos (self=0x0, args=0x82114a4)
    at /mnt/zenglOX/Python-2.7.8/Modules/mathmodule.c:809
809	FUNC1(acos, acos, 0,
(gdb) s
math_1 (arg=0x82114a4, func=0xb7f7a430 <acos>, can_overflow=0)
    at /mnt/zenglOX/Python-2.7.8/Modules/mathmodule.c:686
686	    x = PyFloat_AsDouble(arg);
(gdb) n
687	    if (x == -1.0 && PyErr_Occurred())
(gdb) p x
$1 = 0.59999999999999998
(gdb) n
689	    errno = 0;
(gdb) n
691	    r = (*func)(x);
(gdb) p func
$2 = (double (*)(double)) 0xb7f7a430 <acos>
(gdb) si
0xb7fd3aeb	691	    r = (*func)(x);
(gdb) si
0xb7fd3aee	691	    r = (*func)(x);
(gdb) si
0xb7fd3af1	691	    r = (*func)(x);
(gdb) si
0xb7fd3af4	691	    r = (*func)(x);
(gdb) si
0xb7f7a430 in acos () from /lib/libm.so.6
(gdb) finish 
Run till exit from #0  0xb7f7a430 in acos () from /lib/libm.so.6
0xb7fd3af6 in math_1 (arg=0x82114a4, func=0xb7f7a430 <acos>, can_overflow=0)
    at /mnt/zenglOX/Python-2.7.8/Modules/mathmodule.c:691
691	    r = (*func)(x);
(gdb) n
693	    if (Py_IS_NAN(r)) {
(gdb) p r
$3 = 0.9272952180016123
(gdb) c
Continuing.
0.9272952180016123
[41076 refs]
>>> quit()
[18354 refs]

Program exited normally.
(gdb) q
[email protected]:~$ 


    当上面的math.acos(0.6)语句执行时,就会进入到底层的math_acos对应的C函数,该C函数在源代码里是以FUNC1宏的形式来定义的,该宏展开后,会进入math_1的C函数,该函数里的x为acos的参数0.6(不过由于浮点数在计算机里保存时存在精度误差,因此,上面显示的就是0.59999999999999998的值),这个参数x,接着会由func来计算出反余弦值来。通过gdb调试器的p func命令,可以看到,func其实就是一个函数指针,它指向的是libm.so数学运算库里的acos函数,libm.so是C语言中常用的底层数学运算库,最后由acos函数计算出反余弦值为0.9272952180016123

    由于libm.so默认是不带调试信息的,所以,上面只能用si汇编指令一步步进入到该库中,在查看到acos确实属于libm.so.6的库文件后,再通过finish指令返回。

    因此,前面介绍的acos,asin,atan,atan2,cos,hypot,sin及tan这几个Python函数,最终会通过libm.so里的同名函数来完成具体的三角计算。其实,python一直以来都想摆脱libm库,只是由于不同平台下指令集都不太一样,尝试了很多次都没有成功,至少就目前而言,它最终还是要依赖libm.so来完成具体的数学运算。

    在Linux下,我们可以使用man命令来查看libm.so库里这些同名C函数的具体作用,这样就可以加深对Python中对应的同名函数的理解:

[email protected]:~$ man acos
ACOS(3)                    Linux Programmer's Manual                   ACOS(3)

NAME
       acos, acosf, acosl - arc cosine function

SYNOPSIS
       #include <math.h>

       double acos(double x);
       float acosf(float x);
       long double acosl(long double x);
....................................................
[email protected]:~$ man asin
ASIN(3)                    Linux Programmer's Manual                   ASIN(3)

NAME
       asin, asinf, asinl - arc sine function

SYNOPSIS
       #include <math.h>

       double asin(double x);
       float asinf(float x);
       long double asinl(long double x);
....................................................
[email protected]:~$ man atan
ATAN(3)                    Linux Programmer's Manual                   ATAN(3)

NAME
       atan, atanf, atanl - arc tangent function

SYNOPSIS
       #include <math.h>

       double atan(double x);
       float atanf(float x);
       long double atanl( long double x);
....................................................
[email protected]:~$ man atan2
ATAN2(3)                   Linux Programmer's Manual                  ATAN2(3)

NAME
       atan2, atan2f, atan2l - arc tangent function of two variables

SYNOPSIS
       #include <math.h>

       double atan2(double y, double x);
       float atan2f(float y, float x);
       long double atan2l(long double y, long double x);
....................................................
DESCRIPTION
       The atan2() function calculates the principal value of the arc tangent
       of y/x, using the signs of the two arguments to determine the quadrant
       of the result.
....................................................
[email protected]:~$ man cos
COS(3)                     Linux Programmer's Manual                    COS(3)

NAME
       cos, cosf, cosl - cosine function

SYNOPSIS
       #include <math.h>

       double cos(double x);
       float cosf(float x);
       long double cosl(long double x);
....................................................
[email protected]:~$ man hypot
HYPOT(3)                   Linux Programmer's Manual                  HYPOT(3)

NAME
       hypot, hypotf, hypotl - Euclidean distance function

SYNOPSIS
       #include <math.h>

       double hypot(double x, double y);
       float hypotf(float x, float y);
       long double hypotl(long double x, long double y);
....................................................
[email protected]:~$ man sin
SIN(3)                     Linux Programmer's Manual                    SIN(3)

NAME
       sin, sinf, sinl - sine function

SYNOPSIS
       #include <math.h>

       double sin(double x);
       float sinf(float x);
       long double sinl(long double x);
....................................................
[email protected]:~$ man tan
TAN(3)                     Linux Programmer's Manual                    TAN(3)

NAME
       tan, tanf, tanl - tangent function

SYNOPSIS
       #include <math.h>

       double tan(double x);
       float tanf(float x);
       long double tanl(long double x);
....................................................
[email protected]:~$ 


    在之前介绍的三角函数表格里,degrees与radians函数则没有对应的libm.so的同名C函数,它们是直接在mathmodule.c文件中实现的:

static const double degToRad = Py_MATH_PI / 180.0;
static const double radToDeg = 180.0 / Py_MATH_PI;

static PyObject *
math_degrees(PyObject *self, PyObject *arg)
{
    double x = PyFloat_AsDouble(arg);
    if (x == -1.0 && PyErr_Occurred())
        return NULL;
    return PyFloat_FromDouble(x * radToDeg);
}

....................................................

static PyObject *
math_radians(PyObject *self, PyObject *arg)
{
    double x = PyFloat_AsDouble(arg);
    if (x == -1.0 && PyErr_Occurred())
        return NULL;
    return PyFloat_FromDouble(x * degToRad);
}


    上面的math_degrees是math模块的degrees方法的底层C函数,该函数会通过radToDeg变量,将参数x由弧度值转换为对应的度值(radToDeg表示每弧度对应多少度),math_radians则是math模块的radians方法的底层C函数,它会通过degToRad变量,将参数x由度值转换为对应的弧度值。

    以上就是math模块中的三角函数(或者叫三角方法)的内部实现机制,以下是这些方法的使用实例:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.acos(0.6)
0.9272952180016123
>>> math.asin(0.6)
0.6435011087932844
>>> math.atan(0.6)
0.5404195002705842
>>> math.atan2(6, 10)
0.5404195002705842
>>> math.degrees(0.8)
45.836623610465864
>>> math.radians(45.8366)
0.7999995879196328
>>> math.cos(0.8)
0.6967067093471654
>>> math.hypot(3, 4)
5.0
>>> math.sin(0.8)
0.7173560908995228
>>> math.tan(0.8)
1.0296385570503641
>>> quit()
[email protected]:~$ 


    上面这些函数都是属于math模块的,因此,在使用之前,需要先通过import math语句来导入该模块。

    从math模块的C源文件即Modules/mathmodule.c文件里,可以看到,在math模块初始化时,还会向模块中加入两个属性成员:

PyMODINIT_FUNC
initmath(void)
{
    PyObject *m;

    m = Py_InitModule3("math", math_methods, module_doc);
    if (m == NULL)
        goto finally;

    PyModule_AddObject(m, "pi", PyFloat_FromDouble(Py_MATH_PI));
    PyModule_AddObject(m, "e", PyFloat_FromDouble(Py_MATH_E));

    finally:
    return;
}


    上面的initmath就是math模块的初始化C函数,当导入该模块时,就会调用此函数,在这个函数里,会向math模块加入 pie 两个成员,它们分别对应Py_MATH_PI与Py_MATH_E两个宏值,这两个宏定义在Include/pymath.h的头文件里:

#ifndef Py_MATH_PI
#define Py_MATH_PI 3.14159265358979323846
#endif

....................................................

#ifndef Py_MATH_E
#define Py_MATH_E 2.7182818284590452354
#endif


    因此,成员pi的值为3.14159265358979323846,而成员e的值为2.7182818284590452354,这是数学里的两个常量。有关pi的数学概念,请参考 圆周率_维基百科 该链接对应的文章,有关e的数学概念,请参考 E_(数学常数) 该链接对应的维基百科文章。

    当然,这两个成员的值是可以在Python脚本里被修改的:

[email protected]:~$ python
Python 2.7.8 (default, Feb 20 2015, 12:54:46) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.pi
3.141592653589793
>>> math.e
2.718281828459045
>>> math.pi = 10.10
>>> math.e = 10.10
>>> math.pi
10.1
>>> math.e
10.1
>>> quit()
[email protected]:~$


    上面就将这两个成员的值修改为了10.1,当然,这种修改只是对当前的执行脚本起作用。

本章结束语:

    以上就是Python中三角函数的相关内容。

    限于篇幅,本章先到这里,下一篇将介绍Python里的字符串对象。

    OK,休息,休息一下 o(∩_∩)o~~

    每当一位艺术家逝去,人类的一些幻想也随之而逝。

——  罗斯福
 
上下篇

下一篇: Python字符串类型

上一篇: Python随机数函数

相关文章

Python基本的I/O操作 (一)

Python的time模块

Python基本的I/O操作 (三)

Python循环语句

Python的变量类型

Python相关的数学运算函数