获取无符号 int 或浮点数 (C) 的尾数(浮点数)



因此,我正在尝试编写一个函数,该函数以(尾数* 2^指数)格式打印给定的浮点数(n)。我能够得到符号和指数,但不能得到尾数(无论数字是什么,尾数总是等于 0.000000)。我拥有的是:

unsigned int num = *(unsigned*)&n;
unsigned int m = num & 0x007fffff;
mantissa = *(float*)&m;

关于问题可能是什么的任何想法?

C 库包含一个执行此确切任务的函数,frexp

int expon;
float mant = frexpf(n, &expon);
printf("%g = %g * 2^%dn", n, mant, expon);

另一种方法是使用log2fexp2f

if (n == 0) {
mant  = 0;
expon = 0;
} else {
expon = floorf(log2f(fabsf(n)));
mant = n * exp2f(-expon);
}

对于相同的输入,这两种技术可能会给出不同的结果。 例如,在我的计算机上,frexpf技术将 4 描述为 0.5 × 23,但log2f技术将 4 描述为 1 × 22。 从数学上讲,两者都是正确的。 此外,frexp会给你尾数的确切部分,而log2fexp2f可能会四舍五入最后一两个位。


您应该知道,*(unsigned *)&n*(float *)&m违反了反对"类型双关语"的规则,并且具有未定义的行为。 如果要获取与浮点数具有相同位表示形式的整数,反之亦然,请使用联合:

union { uint32_t i; float f; } u;
u.f = n;
num = u.i;

(注意:大约从2003年开始,这种联合的使用在C中就有了明确的定义,但是,由于C++委员会长期以来的习惯是没有足够关注C的变化,因此在C++中没有正式明确定义。

您还应该知道 IEEE 浮点数使用"有偏差"指数。 当您初始化float变量的尾数字段但将其指数字段保留为零时,这将为您提供具有较大负指数的数字的表示形式:换句话说,一个如此小的数字,printf("%f", n)将其打印为零。 每当printf("%f", variable)打印零时,请将%f更改为%g%a并重新运行程序,然后再假设variable实际上为零。

你正在剥离指数的位,留下 0。指数 0 是特殊的,这意味着该数字是非规范化的并且非常小,位于可表示数字范围的最底部。我想如果你仔细观察,你会发现你的结果并不完全为零,只是太小了,以至于你很难分辨出区别。

为了获得尾数的合理数字,您需要重新输入适当的指数。如果你想要一个在 1.0 到 2.0 范围内的尾数,你需要一个指数 0,但添加偏差意味着你真的需要一个 127 的指数。

unsigned int m = (num & 0x007fffff) | (127 << 23);
mantissa = *(float*)&m;

如果你宁愿有一个完全整数的尾数,你需要一个指数23,偏差它变成150。

unsigned int m = (num & 0x007fffff) | ((23+127) << 23);
mantissa = *(float*)&m;

除了zwol的评论:如果你想自己做,你必须获得一些关于IEEE-754浮子内部的知识。一旦你完成了,你可以写一些类似的东西

#include <stdlib.h>
#include <stdio.h>
#include <math.h>               // for testing only
typedef union {
float value;
unsigned int bits; // assuming 32 bit large ints (better: uint32_t)
} ieee_754_float;

// clang -g3 -O3 -W -Wall -Wextra -Wpedantic -Weverything -std=c11 -o testthewest testthewest.c -lm
int main(int argc, char **argv)
{
unsigned int m, num;
int exp; // the exponent can be negative
float n, mantissa;
ieee_754_float uf;
// neither checks nor balances included!
if (argc == 2) {
n = atof(argv[1]);
} else {
exit(EXIT_FAILURE);
}
uf.value = n;
num = uf.bits;
m = num & 0x807fffff;         // extract mantissa (i.e.: get rid of sign bit and exponent)
num = num & 0x7fffffff;       // full number without sign bit
exp = (num >> 23) - 126;      // extract exponent and subtract bias
m |= 0x3f000000;              // normalize mantissa (add bias)
uf.bits = m;
mantissa = uf.value;
printf("n = %g, mantissa = %g, exp = %d, check %gn", n, mantissa, exp, mantissa * powf(2, exp));
exit(EXIT_SUCCESS);
}

注意:上面的代码是快速和肮脏(tm)物种之一,不适用于生产。它还缺少对次正规(非正常)数字的处理,这是您必须包含的内容。提示:将尾数乘以 2 的大幂(例如:2^25 或在该范围内),并相应地调整指数(如果您取我示例的值减去 25)。

最新更新