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

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) : 1
isfinite(1.0/0.0) : 0
isfinite(-1.0/0.0) : 0
isfinite(sqrt(-1.0)): 0

我们来看看它的具体实现

//glibc/sysdeps/ieee754/flt-32/s_finitef.c29 #ifndef FINITEF30 # define FINITEF __finitef31 #endif32 
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) : 0
isinf(1.0/0.0) : 1
isinf(-1.0/0.0) : 1
isinf(sqrt(-1.0): 0

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

//glibc/sysdeps/ieee754/flt-32/s_isinff.c17 int18 __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,分别如下:

数据

负无限

正无限

0

ix

(1)(11111111)(000...0)

(0)(11111111)(000...0)

(0)(00000000)(000...0)

t = ix & 0x7fffffff,截断指数域和小数域

(0)(11111111)(000...0)

(0)(11111111)(000...0)

(0)(00000000)(000...0)

t ^= 0x7f800000,指数域取反,小数域不变

(0)(00000000)(000...0)

(0)(00000000)(000...0)

(0)(11111111)(000...0)

t|=-t,即t|(t的反码+1)

(0)(00000000)(000...0)|(全1+1)

(0)(00000000)(000...0)|(全1+1)

(0)(11111111)(000...0)|((1)(0..0)(1)+1)

结果

溢出之后全为0

溢出之后全为0

全为1,小数域全为0

~(t >> 31),t是有符号数,最高位用符号位填充

1...11

1...11

0...00

(ix >> 30)

1...11

0...01

0...00

~(t >> 31) & (ix >> 30)

1...11

0...01

0...00

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