C 如何获取浮点数的符号、尾数和指数
我有一个程序,它运行在两个处理器上,其中一个不支持浮点运算。所以,我需要在处理器中使用定点执行浮点计算。为此,我将使用浮点仿真库 我需要首先在支持浮点运算的处理器上提取浮点数的符号、尾数和指数。所以,我的问题是如何得到单精度浮点数的符号、尾数和指数 按照此图的格式 这就是我到目前为止所做的,但除了符号,尾数和指数都不正确。我想,我错过了一些东西C 如何获取浮点数的符号、尾数和指数,c,floating-point,emulation,C,Floating Point,Emulation,我有一个程序,它运行在两个处理器上,其中一个不支持浮点运算。所以,我需要在处理器中使用定点执行浮点计算。为此,我将使用浮点仿真库 我需要首先在支持浮点运算的处理器上提取浮点数的符号、尾数和指数。所以,我的问题是如何得到单精度浮点数的符号、尾数和指数 按照此图的格式 这就是我到目前为止所做的,但除了符号,尾数和指数都不正确。我想,我错过了一些东西 void getSME( int& s, int& m, int& e, float number ) { unsig
void getSME( int& s, int& m, int& e, float number )
{
unsigned int* ptr = (unsigned int*)&number;
s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;
}
将指向浮点变量的指针强制转换为类似于
无符号int
。然后,您可以移动和屏蔽位以获得每个组件
float foo;
unsigned int ival, mantissa, exponent, sign;
foo = -21.4f;
ival = *((unsigned int *)&foo);
mantissa = ( ival & 0x7FFFFF);
ival = ival >> 23;
exponent = ( ival & 0xFF );
ival = ival >> 8;
sign = ( ival & 0x01 );
显然,您可能不会对指数位和符号位使用无符号整数,但这至少会给您一个想法。找出直接支持浮点的CPU上使用的浮点数的格式,并将其分解为这些部分。最常见的格式是 或者,您可以使用一些特殊函数(
double-frexp(double-value,int*exp);
和double-ldexp(double-x,int-exp);
)获取这些部件,如中所示
是将
%a
与printf()一起使用我认为使用联合进行强制转换更好,更清晰
#include <stdio.h>
typedef union {
float f;
struct {
unsigned int mantisa : 23;
unsigned int exponent : 8;
unsigned int sign : 1;
} parts;
} float_cast;
int main(void) {
float_cast d1 = { .f = 0.15625 };
printf("sign = %x\n", d1.parts.sign);
printf("exponent = %x\n", d1.parts.exponent);
printf("mantisa = %x\n", d1.parts.mantisa);
}
#包括
typedef联合{
浮动f;
结构{
无符号整数mantisa:23;
无符号整数指数:8;
无符号整数符号:1;
}零件;
}浮雕;
内部主(空){
浮点数d1={.f=0.15625};
printf(“符号=%x\n”,d1.parts.sign);
printf(“指数=%x\n”,d1.parts.exponent);
printf(“mantisa=%x\n”,d1.parts.mantisa);
}
基于的示例,您的&
输入了错误的位。我想你想要:
s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;
请记住,当您&
时,您正在将未设置的位归零。在这种情况下,当你得到指数时,你想把符号位置零,当你得到尾数时,你想把符号位和指数置零
请注意,遮罩直接来自您的图片。因此,指数掩码将如下所示:
0 11111110000000000000000000000000000
尾数面具看起来像:
00000000 11111111111111
我的建议是坚持规则0,如果这足够的话,不要重做标准库已经做的事情。看看math.h(标准C++中的cmath)和函数frexp、frexpf、frexpl,它们在有效位和指数部分中断浮点值(double、float或long double)。要从有效位提取符号,您可以使用signbit,也可以在math.h/cmath或copysign(仅限C++11)中使用。一些语义稍有不同的替代方案是modf和ilogb/scalbn,可在C++11中使用;比较了它们,但我在文档中没有找到所有这些函数在+/-inf和NAN中的行为。最后,如果您确实想使用位掩码(例如,您迫切需要知道确切的位,并且您的程序可能具有不同表示形式的不同NAN,并且您不信任上述函数),至少通过使用float.h/cfloat中的宏使所有内容独立于平台。在Linux软件包中,glibc headers提供了带有浮点类型定义的header#include
,例如:
union ieee754_double
{
double d;
/* This is the IEEE 754 double-precision format. */
struct
{
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int negative:1;
unsigned int exponent:11;
/* Together these comprise the mantissa. */
unsigned int mantissa0:20;
unsigned int mantissa1:32;
#endif /* Big endian. */
#if __BYTE_ORDER == __LITTLE_ENDIAN
# if __FLOAT_WORD_ORDER == __BIG_ENDIAN
unsigned int mantissa0:20;
unsigned int exponent:11;
unsigned int negative:1;
unsigned int mantissa1:32;
# else
/* Together these comprise the mantissa. */
unsigned int mantissa1:32;
unsigned int mantissa0:20;
unsigned int exponent:11;
unsigned int negative:1;
# endif
#endif /* Little endian. */
} ieee;
/* This format makes it easier to see if a NaN is a signalling NaN. */
struct
{
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int negative:1;
unsigned int exponent:11;
unsigned int quiet_nan:1;
/* Together these comprise the mantissa. */
unsigned int mantissa0:19;
unsigned int mantissa1:32;
#else
# if __FLOAT_WORD_ORDER == __BIG_ENDIAN
unsigned int mantissa0:19;
unsigned int quiet_nan:1;
unsigned int exponent:11;
unsigned int negative:1;
unsigned int mantissa1:32;
# else
/* Together these comprise the mantissa. */
unsigned int mantissa1:32;
unsigned int mantissa0:19;
unsigned int quiet_nan:1;
unsigned int exponent:11;
unsigned int negative:1;
# endif
#endif
} ieee_nan;
};
#define IEEE754_DOUBLE_BIAS 0x3ff /* Added to exponent. */
不要让函数做多种事情
不要遮掩然后转移;先移位,然后遮罩
不要不必要地改变值,因为它速度慢、缓存破坏和容易出错
不要使用魔法数字
/*NaNs、无穷大、未处理的非规范化*/
/*假设sizeof(float)==4,并使用ieee754二进制32格式*/
/*假设两个s-补码机器*/
/*C99*/
#包括
#定义符号(f)((f)有关要提取的联合类型,请参见此标题:float
、double
和long double
,(endianness handled)。以下是一个提取:
/*
** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** Single Precision (float) -- Standard IEEE 754 Floating-point Specification
*/
# define IEEE_754_FLOAT_MANTISSA_BITS (23)
# define IEEE_754_FLOAT_EXPONENT_BITS (8)
# define IEEE_754_FLOAT_SIGN_BITS (1)
.
.
.
# if (IS_BIG_ENDIAN == 1)
typedef union {
float value;
struct {
__int8_t sign : IEEE_754_FLOAT_SIGN_BITS;
__int8_t exponent : IEEE_754_FLOAT_EXPONENT_BITS;
__uint32_t mantissa : IEEE_754_FLOAT_MANTISSA_BITS;
};
} IEEE_754_float;
# else
typedef union {
float value;
struct {
__uint32_t mantissa : IEEE_754_FLOAT_MANTISSA_BITS;
__int8_t exponent : IEEE_754_FLOAT_EXPONENT_BITS;
__int8_t sign : IEEE_754_FLOAT_SIGN_BITS;
};
} IEEE_754_float;
# endif
有关如何将double
值转换为字符串形式的演示,请参阅
此外,请参阅的一节,它非常好地简单地解释了所有浮点类型的内存表示/布局,以及如何按照IEEE 754浮点规范对其进行解码(带插图)
它还有指向真正优秀的资源的链接,这些资源的解释更深入。试着从这里开始:,但我几乎可以肯定的是,通过指针转换的别名在C标准中不受支持,在某些编译器中可能会很麻烦。最好使用(union{float f;uint32_t u;}{number}.u
。这将返回一个uint32_t
,即float
number
的字节,重新解释为32位无符号整数。我假设IEEE 754为32位二进制。您是否注意到以下问题?(1)通过将127添加到实际指数,对指数进行了偏置。(2)除了非常小的浮点,所有的都是标准化的,并且标准化浮点尾数的前导1位不被存储。你是指C还是C++(C没有引用,只有指针)三个问题:0。没有从编码的指数1中移除偏差。没有为正常的非零数添加隐式尾数2。没有处理非规范、无穷和sNaN/qNaNs。我知道,这就是我所做的,但我没有得到正确的值。请参阅我编辑的帖子。未定义behaviour@MetallicPriest现在试试,我第一次戴错面具了时间。那么所谓的隐藏位呢?我没有看到任何人设置它:m |=0x00800000;
。请注意,应该检查数字是否有特殊值(非规范、NaN、无穷大)首先,因为这些需要不同的处理方法。@RudyVelthuis来自他们的原始代码,看起来他们并没有试图实际获得指数和ma的值