Amethyst Studio
872 words
4 minutes
glibc源码剖析 - isinf

函数声明与功能#

函数声明:

int isinf(float x);

函数功能,判断给定的xx是否是无穷大,如果不是,返回0,是-\infin,返回-1,是++\infin,返回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/
Author
Kaida Amethyst
Published at
2022-02-12