写点什么

C++ 学习 ------cmath 头文件的源码学习 02

作者:桑榆
  • 2022 年 9 月 09 日
    广东
  • 本文字数:1566 字

    阅读完需:约 5 分钟

续接上文:https://xie.infoq.cn/article/a8610035745c237f4b2cf4863

宏函数 isfinite 和 isinf 的分析

宏函数定义---分类函数

isfinite---返回输入数 x 是否有限值

我们来看几个例子,其它三个数都是无限或无法解释的实数:

  printf ("isfinite(0.0)       : %d\n",isfinite(0.0));  printf ("isfinite(1.0/0.0)   : %d\n",isfinite(1.0/0.0));  printf ("isfinite(-1.0/0.0)  : %d\n",isfinite(-1.0/0.0));  printf ("isfinite(sqrt(-1.0)): %d\n",isfinite(sqrt(-1.0)));  //测试结果:isfinite(0.0)       : 1isfinite(1.0/0.0)   : 0isfinite(-1.0/0.0)  : 0isfinite(sqrt(-1.0)): 0
复制代码

我们来看看它的具体实现

//glibc/sysdeps/ieee754/flt-32/s_finitef.c 29 #ifndef FINITEF 30 # define FINITEF __finitef 31 #endif 32  33 int FINITEF(float x) 34 { 35     int32_t ix; 36     GET_FLOAT_WORD(ix,x); 37     return (int)((uint32_t)((ix&0x7f800000)-0x7f800000)>>31); 38 }
复制代码

实现方式与 ieee754 对浮点数的解释一致,首先转换获取该浮点数的位解释(第 31 位符号位,30 到 23 位为指数域,22 到 0 位为小数域);

(ix&0x7f800000)是截取其指数域,然后减去 0x7f800000 得到两者的差,然后左移 31 位,相当于此时只保留第 32 位的值。因为指数域值为 0-255,其中 0-254 做减法减去 255 之后都是不够的,会导致最高位变为 1,这时所表示的浮点数也正式有限的,而指数域全为 1 就会导致 255-255,最高位为 0,最后 return 也是 0,此时表示的数也是无穷大的。

isinf---返回输入数 x 是否是无限值

还是上面的例子:

  printf ("isinf(0.0)       : %d\n",isinf(0.0));  printf ("isinf(1.0/0.0)   : %d\n",isinf(1.0/0.0));  printf ("isinf(-1.0/0.0)  : %d\n",isinf(-1.0/0.0));  printf ("isinf(sqrt(-1.0)): %d\n",isinf(sqrt(-1.0)));  //测试结果isinf(0.0)      : 0isinf(1.0/0.0)  : 1isinf(-1.0/0.0) : 1isinf(sqrt(-1.0): 0
复制代码

这一次两个除 0 的情况都是无限值,函数逻辑如下:

//glibc/sysdeps/ieee754/flt-32/s_isinff.c 17 int 18 __isinff (float x) 19 { 20     int32_t ix,t; 21     GET_FLOAT_WORD(ix,x); 22     t = ix & 0x7fffffff; 23     t ^= 0x7f800000; 24     t |= -t; 25     return ~(t >> 31) & (ix >> 30); 26 }
复制代码

判断逻辑与 isfinite 类似,t = ix & 0x7fffffff 是获取指数域+小数域;

t ^= 0x7f800000 按位异或,对指数域,只有为 0 的位置异或之后为 1,为 1 的位置异或之后为 0,小数域为 0 的位置异或之后为 0,为 1 的位置异或之后为 1,相当于不改变小数域,指数域按位取反;

t |= -t 按位或上-t,注意到这里使用的是 int32_t,所以转换为负数之后使用补码表示,即负数的补码等于原来正数的反码+1,这样看来-t,就是对原来的 t,符号域为 1(因为 t 的符号域截断为 0),指数域再按位取反,即变为最开始的指数域,小数域按位取反,然后再加 1;

这里我们扩展思考一下:二进制下的数字都可以写成(A)1(B)的形式,其中 A 表示一串 01 字符串,1 表示从右向左的出现的第一个数字 1,B 表示空(奇数)或者是连续的 0(偶数),即:

  • 偶数:(A)1(00…0)

  • 奇数:(A)1 那么,-t 的运算是,所有位置取反+1,即变形如下(Ā表示所有位置取反):

  • 偶数:(Ā)0(11…1) + 1 = (Ā)1(00…0)

  • 奇数:(Ā)0 + 1 = (Ā)1 那么,t|(-t),就是

  • 偶数:(Ā)1(00…0) | (A)1(00…0) = (11…1)1(00…0)

  • 奇数:(Ā)1 | (A)1 = (1…1)1 所以最后 t 要么全为 1,要么一堆 1 跟着一堆 0;这里我们考虑无限函数判断的三种情况:负无限,正无限,0,分别如下:

这样来看,中间位运算的作用就是将无限数的特征(指数域全为 1,小数域全为 0)提取出来,然后利用原数据的符号位和指数域最高位将数据转换为正负 1 标识正无限和负无限,同时也可以将 0 表示为 0,这也是符合该函数的返回值定义的,如果返回来其它值,那说明并不满足上面的特征,那就不是无限数。

发布于: 2022 年 09 月 09 日阅读数: 98
用户头像

桑榆

关注

北海虽赊,扶摇可接;东隅已逝,桑榆非晚! 2020.02.29 加入

Android手机厂商-相机软件系统工程师 爬山/徒步/Coding

评论

发布
暂无评论
C++学习------cmath头文件的源码学习02_c++_桑榆_InfoQ写作社区