如何计算浮点数中设置的位数?

5

如何使用C函数计算浮点数中设置的位数?


性能度量? - Jek
5个回答

6
#include <stdio.h>  /* for printf() */
#include <limits.h> /* for CHAR_BIT */

int main(void) {
  /* union method */
  {
    /* a union can only be initialized for the first option in the union */
    union { float f; char cs[sizeof(float)]; } const focs = { 1.0 };
    int j,k;
    int count = 0;
    for (j = 0; j < sizeof(float); j++)
    {
      char const byte = focs.cs[j];
      for (k = 0; k < CHAR_BIT; k++)
      {
        if ((1 << k) & byte)
        {
          count++;
        }
      }
    }
    printf("count(%2.1f) = %d\n", focs.f, count);
  }
  /* cast method */
  {
    float const f = 2.5;
    int j,k; 
    int count = 0;
    for (j = 0; j < sizeof(float); j++)
    {
      char const byte = ((char *)&f)[j];
      for (k = 0; k < CHAR_BIT; k++)
      {
        if ((1 << k) & byte)
        {
          count++;
        }
      }
    }
    printf("count(%2.1f) = %d\n", f, count);
  }
  return 0;
}

我忘记了联合体技巧!但是你应该使用sizeof(float)而不是sizeof(int)。此外,在大多数系统上,int和float的大小相同,但我找不到任何关于它们相对大小(或任何关于float大小)的标准,所以我不能确定 - 但只有一个for()循环并使用float和int的联合体不是更好吗? - Chris Lutz
这就是问题所在-我认为在实现方面,它们几乎总是相同的,但我不知道是否有任何标准来指定,这就是为什么我这样做的原因。 我仍然可以通过执行 int i; int count=0; for (i = 0; i <8 * sizeof(float); i ++){if ((1 <<(i%8))&focs.cs [i / 8]){count ++;}} 来压缩两个循环。 - rampion
虽然通过这个(https://dev59.com/-HNA5IYBdhLWcg3wpfiu),我应该使用`CHAR_BIT`代替8 (尽管我可以放心地假设 sizeof(char) == 1,因为这是通过定义的)。 - rampion
1
sizeof(char) 的值始终为1,但是你的代码理论上可能会在PDP-7上运行,其中CHAR_BIT的值为18(!)。 - Chris Lutz

3

如果你想要处理浮点数的位表示,你可以像这样操作:

float f; /* whatever your float is */
int i = *(int *)&f;

这段代码使用取地址符号&获取到变量f的地址,该地址类型为float *,即指向float类型的指针。然后使用(int *)重新解释该指针,意思是“假装这个指针不再指向float,而是指向一个int”。请注意,它并没有改变f的值。最后一个*(或者说第一个,因为我们从右往左读)对这个指向int的指针进行了解引用,因此返回了一个int,也就是与float具有相同二进制表示的整数。
要执行相反操作,将int i 转换回 float f,则执行相反的操作:
f = *(float *)&i;

除非我搞错了,这个操作在C标准中是未定义的,但可能在大多数计算机和编译器上都有效。这是未定义的,因为实际浮点数表示的数字取决于实现,可以由CPU或编译器处理,因此在此操作后几乎不可能预测i的值(反向操作中f的值也是如此)。它被广泛地用于John Carmack的倒数平方根函数中,目的相同。
无论如何,如果您正在真正的代码中执行此操作,您应该停下来仔细考虑您想要做什么以及为什么要使用float。但是,如果您只是出于好奇心而这样做,或者您已经考虑过这些问题并且确定了您的设计和方法,请去尝试一下。
我认为您已经知道如何计算常规整数中设置的位数,因为这是一个更容易的任务。如果您不知道,您的编译器(或C语言,我甚至不知道)可能有一个计算位数的函数,或者您可以从精彩的Bit-Twiddling Hacks网站中使用位运算的方法来完成这样的事情(这应该相当快)。

1
这不仅是因为这个原因导致未定义 - GCC(在某些配置中)会发出警告解引用类型转换指针将破坏严格别名规则,有充分的理由,因为这确实打破了假设(从ISO C获得性能优势),即两个指向不同简单类型(除了char)的指针永远不会指向同一对象。 - ephemient
3
@sigjuice:这两个指针是通过 &f 创建的 float* 和通过将该 float* 强制转换而成的 int*。编译器完全有权重新排列内存访问操作,以便在初始化 f 之前将内存位置读入 i,这就是行为未定义的原因。 - Steve Jessop
请使用memcpyunion。强制转换*(float*)&i违反了严格别名规则。在C和C++标准中可靠地进行类型转换 - Peter Cordes

3

第一个答案提到了一种计算整数中设置位的好方法:

int NumberOfSetBits(int i)
{
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    return ((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
}

要在您的浮动元素上使用它,您可以像这样操作:
//...
float f;
//...
int numBitsOfF = NumberOfSetBits(*(int*) &f);

在C语言中,您需要使用memcpyunion来安全地进行类型转换。不要强制转换指针;这是严格别名UB。可靠地在C和C++标准之间进行类型转换。对于实际的popcount,可以使用像GNU C __builtin_popcount这样的内置函数。计算32位整数中设置位的数量 - Peter Cordes

2

您是指IEEE-754单精度表示中设置的位吗?如果是这样,请将其转换为int(float和int均为32位宽),然后进行常规位计数:SO问题#109023


2
强制转换为整数将截断并转换,而不是给您原始表示。 - bdonlan
2
好主意。那么更好的解决方案是将其联合或浮动 f; (int)&f。 - Adrian Panasiuk
1
大家好,如果我能得到一些示例就太好了。 - Maddy
1
https://dev59.com/23VD5IYBdhLWcg3wDG_m - Adrian Panasiuk
1
我会使用*(unsigned int*)&f。 无符号类型的表现更好(没有符号混乱)。 - Johannes Schaub - litb
在C语言中,您需要使用memcpyunion来安全地进行类型转换。不要强制转换指针;这是严格别名UB。可靠地跨越C和C++标准进行类型转换 - Peter Cordes

-1
The following function will find the number of bits in a 32-bit number. Just type case your float with integer and call this function by a cast 
float f=3.14f;
count_bits(*(int *)&f);

int count_bits(int v)
{
    // count the number of bits set in v
    int c; // c accumulates the total bits set in v
    int b=v;
    for (c = 0; v; c++)
    {
            v &= v - 1; // clear the least significant bit set
    }
    //printf("No of bits in %d is %d\n",b,c);
    return c;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接