在c中获取浮点数的指数

getting exponent of a floating number in c

抱歉,如果有人问过这个问题,我已经看到了其他提取浮点数指数的方法,但是这是给我的:

unsigned f2i(float f)
{
  union {
    unsigned i;
    float f;
  } x;
  x.i = 0;
  x.f = f;
  return x.i;
}

我无法理解这个联合数据类型,因为最后的 return x.i 不应该总是使 f2i return 为 0 吗?

此外,这种数据类型甚至可以用于什么应用程序?例如,假设我有一个函数:

int getexponent(float f){
}

这个函数应该得到偏差为 127 的浮点数的指数值。我已经找到了很多方法来实现这一点,但是我如何操纵 f2i 函数来达到这个目的?

感谢任何指点!

更新!! 哇,多年后,这看起来微不足道。 有兴趣的小伙伴可以看一下函数!

int getexponent(float f) {
    unsigned f2u(float f); 
 unsigned int ui = (f2u(f)>>23) & 0xff ;//shift over by 23 and compare to 0xff to get the exponent with the bias 
 int bias = 127;//initialized bias 
 if(ui == 0) return 1-bias; // special case 0
 else if(ui == 255) return 11111111; //special case infinity
 return ui - bias;
}

因为它是联合,这意味着 x.ix.f 具有相同的地址,这允许您将一种数据类型重新解释为另一种数据类型。在这种情况下,并集首先被 x.i = 0; 清零,然后用 f 填充。然后返回 x.i,它是浮点数 f 的整数表示。如果您随后移动该值,您将获得原始 f 的指数,因为 float 在内存中的布局方式。

I'm having trouble understanding this union datatype, because shouldn't the return x.i at the end always make f2i return a 0?

x.i = 0; 行有点偏执,应该没有必要。鉴于 unsigned intfloat 都是 32 位,联合会在内存中创建 一个 32 位 的块,您可以将其作为 float 或作为 float 的纯二进制表示,这就是 unsigned 的用途。 (使用uint32_t会更好。)

这意味着行 x.i = 0;x.f = f; 两次写入同一内​​存区域。

你在函数之后得到的是浮点数的纯二进制表示法。从那里解析出指数或任何其他部分是非常多的实现定义的,因为它取决于浮点格式和字节顺序。 How to represent FLOAT number in memory in C 可能会有帮助。

I'm having trouble understanding this union datatype

联合数据类型是程序员指示某个变量可以是多种不同类型之一的一种方式。 C11标准的措辞类似于"a union contains at most one of its members"。它用于逻辑上可能是一回事的参数之类的东西。例如,IP 地址可能是 IPv4 地址或 IPv6 地址,因此您可以按如下方式定义地址类型:

struct IpAddress
{
    bool isIPv6;
    union 
    {
        uint8_t v4[4];
        uint8_t v6[16];
    } bytes;
}

你会像这样使用它:

struct IpAddress address = // Something
if (address.isIPv6)
{
    doSomeV6ThingWith(address.bytes.v6);
}
else 
{
    doSomeV4ThingWith(address.bytes.v4);
}

历史上,unions 也被用于将一种类型的位获取到另一种类型的对象中。这是因为,在联合中,成员都从相同的内存地址开始。如果我这样做:

float f = 3.0;
int i = f;

编译器将插入代码将浮点数转换为整数,因此指数将丢失。然而,在

union 
{
    unsigned int i;
    float f;
} x;

x.f = 3.0;
int i = x.i;

i 现在包含在 float 中表示 3.0 的确切位。或者至少你希望如此。 C 标准中没有任何内容表明 floatunsigned int 必须具有相同的大小。 C 标准中也没有任何内容要求 float 的特定表示(好吧,附件 F 说浮点数符合 IEC 60559 ,但我不知道这是否算作标准的一部分)。所以上面的代码充其量是不可移植的。

要获得 float 的指数,可移植的方法是 math.h

中定义的 frexpf() 函数

how could I manipulate the f2i function to serve this purpose?

让我们假设 float 以 IEC 60559 格式以 32 位存储,Wkipedia 认为这与 IEEE 754 相同。我们还假设整数以小端格式存储。

union 
{
    uint32_t i;
    float f;
} x;

x.f = someFloat;
uint32_t bits = x.i;

bits 现在包含浮点数的位模式。一个单精度浮点数看起来像这样

SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
^        ^                     ^
bit 31   bit 22                bit 0

其中S为符号位,E为指数位,M为尾数位。

所以得到你的 int32_t 你只需要做一些移位和掩蔽:

uint32_t exponentWithBias = (bits >> 23) & 0xff;

强烈建议不要使用该联合类型,因为它强烈依赖于体系结构和编译器实现....这两件事几乎不可能确定实现您请求的信息的正确方法。

有一些可移植的方法可以做到这一点,所有这些方法都必须处理以 10 为底的对数计算。如果你得到 log10(x) 的整数部分,你就会得到你想要的数字,

int power10 = (int)log10(x);

double log10(double x)
{
     return log(x)/log(10.0);
}

将给你 10 的指数以得到数字乘以尾数得到数字....如果你将原始数字除以最后的结果,你将得到尾数。

小心,因为浮点数通常在内部存储为二的幂,这意味着您存储的指数不是十的幂,而是二的幂。