【问题标题】:getting exponent of a floating number in c在c中获取浮点数的指数
【发布时间】:2017-09-21 08:45:09
【问题描述】:

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

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

我无法理解这个联合数据类型,因为最后的 return x.i 不应该总是让 f2i 返回 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.i和x.f共享同一个数据空间。要提取指数,您还需要对 unsigned 值进行移位和屏蔽。
  • 仅供参考:frexp
  • @Lundin 我认为希望x.i. 在末尾包含与x.f 相同的位。初始的x.i = 0 可能是为了确保如果unsigned int 大于float,则未使用的位被清零。
  • @JeremyP 我相信如果 unsigned int 大于 float 并且使用大端序,结果将是垃圾。此代码需要使用uint32_t 或一些关于类型大小的静态断言。
  • @Lundin 我同意。另外,float 是否保证为四个字节?

标签: c numbers floating


【解决方案1】:

我无法理解这个联合数据类型

联合数据类型是程序员指示某个变量可以是多种不同类型之一的一种方式。 C11 标准的措辞类似于“一个联合最多包含一个成员”。它用于诸如在逻辑上可能是一件事或另一件事的参数之类的事情。例如,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 标准中没有规定 float 和 unsigned int 必须具有相同的大小。 C 标准中也没有任何内容要求 float 的特定表示(嗯,附件 F 说浮点数符合 IEC 60559 ,但我不知道这是否算作标准的一部分)。所以上面的代码充其量是不可移植的。

要获得float 的指数,可移植的方法是在math.h 中定义的frexpf() 函数

我怎样才能操纵 f2i 函数来达到这个目的?

让我们假设 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;

【讨论】:

  • 对不起,能解释一下 (bits >> 23) & 0xff 部分吗?我猜它会查看前 23 位并省略第一位?
  • @NamBurger 这只是意味着将位右移 23 位,以便指数从位 0 开始,然后用二进制 11111111 去除符号位,只留下指数。
【解决方案2】:

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

【讨论】:

  • 绝对清楚,这种技术是完全不可移植的,因为它在实现中依赖于浮点和整数的底层表示。
【解决方案3】:

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

x.i = 0; 行有点偏执,不应该是必要的。鉴于 unsigned int 和 float 都是 32 位,联合在内存中创建 一个 32 位的块,您可以将其作为 float 或作为float,这就是 unsigned 的用途。 (最好使用uint32_t。)

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

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

【讨论】:

    【解决方案4】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-16
      • 1970-01-01
      • 2018-09-23
      • 1970-01-01
      • 2019-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多