872 words
4 minutes
glibc源码剖析 - isinf
函数声明与功能
函数声明:
int isinf(float x);
函数功能,判断给定的是否是无穷大,如果不是,返回0,是,返回-1,是,返回1。
这个函数的功能很明确。代码虽然很简短,但是很巧妙,因此也做一个解析。
需要IEEE754 浮点数标准的知识。
Code
int isinf(float x) {
int ix, t;
ix = *(int*)&x;
t = ix & 0x7fffffff;
t ^= 0x7f800000;
t |= -t;
return ~(t >> 31) & (ix >> 30);
}
源码解析
先说一下这个的基本思路。首先是把x的符号去掉,然后将x与0x7f800000
做一个异或,如果x是无穷大的话,那么得到的值一定是0,它的负数也会是0,将这个数与它的负数做或运算,自然也得到0。也就是说,此时如果得到0,那么它一定是无穷大,然后再去考虑符号就好了。
如果x不是无穷大,那么去掉符号后它与0x7f800000做异或,结果一定不是0,那么它的负数,最高位一定是一个1,将这个数与它的负数做或运算,得到值最高位也必然为1,这就是x不是无穷大的信号。然后返回0就好。
更具体的解析看下面:
int isinf(float x) {
int ix, t;
// ix获取x的十六进制形式
ix = *(int*)&x;
// 与0x7fffffff做按位与,得到一个去掉符号的ix值。记为t
t = ix & 0x7fffffff;
// 然后将这个t与0x7f800000做异或,这个时候,有两种情况
// 1. x是无穷大,那么此时t一定等于0x7f800000,异或完之后,t=0
// 2. x不是无穷大,那么此时t是一个正数
t ^= 0x7f800000;
// 还是按上面的两种情况
// 1. t = 0,-t同样也等于0,注意它的最高位是0!
// 2. t > 0,-t就是一个正常的负数,它的最高位是1!
// 实际上到这里就能明白,只需要根据这个-t就可以知道是不是无穷大了
// 如果-t是0(最高位是0),x是无穷大
// 如果-t小于0(最高位是1),x不是无穷大。
// t |= -t,实际上根据后面的(t>>31),就知道仅仅关注最高位,
// t的最高位一定是0,-t则是0或1,因而t|-t的最高位也是0或1
// 但实际上只需要t=-t即可
t |= -t;
// 如果x是无穷大,那么此时t为0,t>>31就是0,再取反就是-1,也就是ffffffff。
// 它其实是指示后面的ix >> 30要生效。
// 如果x不是无穷大,那么此时t为负数,最高位是1,t>>31就是0xffffffff
// 再取反就是0,此时一定return 0,就不用管后面的ix >> 30了。
// 如果x是无穷大,此时还需要决定是返回1还是-1,注意正负无穷大的差别在于,
// 正无穷大的第一个十六进制是0x7,头两个二进制位是0b01
// 负无穷大的第一个十六进制是0xf,头两个二进制位是0b11
// 所以这里让ix >> 30,如果是正无穷大,那么ix >> 30得到的就是1。
// 如果是负无穷大,那么ix >> 30得到是0xffffffff,就是-1。
// 这个数再与前面的全1做与运算,得到的原值1或-1。
// 这样就计算完成了。
return ~(t >> 31) & (ix >> 30);
}
glibc源码剖析 - isinf
https://ziyue.cafe/posts/glibc-libm-isinf/