3 Math基础类初探
概览,Math基础类封装了常用的数学公式,特别的,提供了对角度和弧度的支持。该函数封装了一些使用汇编语言实现的基础数学类,因此效率还是有保证的。汇编的函数在asm_math.h头文件中被定义,不过汇编那个对俺还是太高深了暂时放弃……。
3.1几个公有静态变量的定义技巧:
const Real Math::POS_INFINITY = std::numeric_limits<Real>::infinity();
const Real Math::NEG_INFINITY = -std::numeric_limits<Real>::infinity();
const Real Math::PI = Real( 4.0 * atan( 1.0 ) );
const Real Math::TWO_PI = Real( 2.0 * PI );
const Real Math::HALF_PI = Real( 0.5 * PI );
const Real Math::fDeg2Rad = PI / Real(180.0);
const Real Math::fRad2Deg = Real(180.0) / PI;
这些变量分别是正无穷,负无穷,派,2派,二分之一派,角度到弧度的变换常数,弧度到角度的变换常数。首先是前两个,在std里定义的实数的上限,注意,这个Real型是在OgrePrerequisites.h中定义的,他可能是double也可能是单精度浮点数。但是通常来讲是一个单精度浮点数。
std::numeric_limits<Real>::infinity()返回预定义“正无穷大”,无穷大和std::numeric_limits<Real>::max()的定义是有区别的,max仅仅是T类型能表示的最大的数而已,顺便说一下numeric_limits这个模板类还提供了以下一些东东:【摘自CSDN】。不过里面常用的东西貌似并不多……。知道有这么个东西就行了,需要的时候现查也来得及。需要注意的是,std::numeric_limits<float>::max()返回的其实是一个整数,这是为啥呢?意思是,返回的数是float能表示的精度最大的“最小的正数”,如果你真的需要一个最小的float数,使用
- std::numeric_limits<float>::max()即可。
digits10 | 返回目标类型在十进制下可以表示的最大位数 |
epsilon | 返回目标数据类型能表示的最逼近1的正数和1的差的绝对值 |
has_denorm | 测试目标类型是不是可以非规范化表示示 |
has_denorm_loss | 测试所有类型是不是能测出因为非规范化而造成的精度损失(不是因为结果本身的不精确) |
has_infinity | 测试目标类型是不是能表示无限(比如被0除,或者其他一些情况) |
has_quiet_NaN | 检查目标类型是不是支持安静类型的NaN |
has_signaling_NaN | 检查目标类型是不是支持信号类型的NaN |
infinity | 检查目标类型的无限类型(如果支持无限表示) |
is_bounded | 检查目标类型的取值是否有限 |
is_exact | 测试目标类型的计算结果是不是不会造成舍入误差(比如float是0) |
is_iec559 | 测试目标类型是不是符合IEC559标准 |
is_integer | 测试目标类型是不是可以用整型来表示(比如char是1,float是0) |
is_modulo | 测试目标类型是否可以用模来表示。 |
is_signed | 测试目标类型是否是带符号的 |
is_specialized max | 测试目标类型是不是在numeric_limits .模板类中有特殊定义 返回可取的有限最大值 |
max_exponent | Returns the maximum positive integral exponent that the floating-point type can represent as a finite value when a base of radix is raised to that power. |
max_exponent10 | Returns the maximum positive integral exponent that the floating-point type can represent as a finite value when a base of ten is raised to that power. |
min | 返回可取的最小值(规范化) |
min_exponent | Returns the maximum negative integral exponent that the floating-point type can represent as a finite value when a base of radix is raised to that power. |
min_exponent10 | Returns the maximum negative integral exponent that the floating-point type can represent as a finite value when a base of ten is raised to that power. |
quiet_NaN | 返回目标类型的安静NAN的表示 |
radix | Returns the integral base, referred to as radix, used for the representation of a type. |
round_error | 返回目标类型的最大可能的舍入误差 |
round_style | 返回一个值,描述不同的方法,其实现可以将浮点数舍入成整数。 |
signaling_NaN | 返回目标类型关于信号NAN的表示 |
tinyness_before | 测试目标类型是不是能测定出微小的舍入误差 |
traps | Tests whether trapping that reports on arithmetic exceptions is implemented for a type. |
3.2 sinTable,tanTable
这里是一个空间换时间的技巧——使用三角函数表的方式实现快速求Sin和Tan。既然做源码分析,那么就要学习高手们的技巧~。
原理是这样子的,以sin为例,首先定义一个静态的sin表,之所以设置成静态,是为了节省空间!
头文件:static Real* mSinTable;
CPP初始化:Real *Math::mSinTable = NULL;
在构造Math类的时候,需要传入一个刻度表的容量trigTableSize,也就是三角函数表的容量,这个数越大,刻度越细,求出来的sin值越精确,但是消耗的空间也就越大,反之类推,亦然。然后根据这个trigTableSize计算每一个三角函数表的跨度。mTrigTableFactor,随后为Real* mSinTable分配空间。
mSinTable = OGRE_ALLOC_T(Real, mTrigTableSize, MEMCATEGORY_GENERAL);
这个OGRE_ALLOC_T看起来很恐怖,封装了好几层,追溯了半天也没追出来,不过简单的说就是一个线程安全的内存分配函数,这个以后还得再研究研究,不过今天的重点不是他。
分配好空间之后,就要计算这个三角函数表了。方法为
void Math::buildTrigTables(void)
{
// Build trig lookup tables
// Could get away with building only PI sized Sin table but simpler this
// way. Who cares, it'll ony use an extra 8k of memory anyway and I like
// simplicity.
Real angle;
for (int i = 0; i < mTrigTableSize; ++i)
{
angle = Math::TWO_PI * i / mTrigTableSize;
mSinTable[i] = sin(angle);
mTanTable[i] = tan(angle);
}
}
这个方法定义了一个2PI容量的正弦三角函数表,据大牛说是为了方便起见,为了节省空间也可以只定义一个PI容量的三角函数表,不过查起表来就比较麻烦了。
其他一些东西,还有包括常用三角函数,求指数,求平方根,求平方根倒数,求点是否在三角形内,求线面是否相交,球体和面,立方体是否相交等等很多和查询有关的数学基础函数。常用函数举例:如Sign方法,求一个浮点数的符号。如果大于0.0,返回1.0,小于0.0,返回-1.0
这个类其实已经很底层了,不过分析Ogre源码的第一步是分析其架构,所以算法的分析和研究放到以后再头疼吧。