我有一个程序,它在两个处理器上运行,其中一个不支持浮点。因此,我需要在该处理器中使用定点执行浮点计算。为此,我将使用浮点仿真库。
我需要首先在支持浮点的处理器上提取浮点数的符号、尾数和指数。所以,我的问题是如何获得单精度浮点数的符号、尾数和指数。
按照该图的格式,
https://i.stack.imgur.com/tPPeM.jpg
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;
}
(union { float f; uint32_t u; }) { number } .u
。这将返回一个 uint32_t
,它是重新解释为 32 位无符号整数的 float
number
的字节。
我认为最好使用工会来做演员,这更清楚。
#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);
}
基于 http://en.wikipedia.org/wiki/Single_precision 的示例
我的建议是坚持规则 0,而不是重做标准库已经做的事情,如果这已经足够了。查看 math.h(标准 C++ 中的 cmath)和函数 frexp、freexpf、freexpl,它们在有效数和指数部分破坏浮点值(double、float 或 long double)。要从有效数字中提取符号,您可以使用符号位,也可以在 math.h / cmath 或 copysign(仅限 C++11)中使用。一些语义稍有不同的替代方案是 modf 和 ilogb/scalbn,在 C++11 中可用; http://en.cppreference.com/w/cpp/numeric/math/logb 比较了它们,但我没有在文档中找到所有这些函数在 +/-inf 和 NaN 下的行为。最后,如果您真的想使用位掩码(例如,您迫切需要知道确切的位,并且您的程序可能有不同的 NaN 具有不同的表示形式,并且您不信任上述函数),至少让一切都与平台无关通过使用 float.h/cfloat 中的宏。
找出直接支持浮点的 CPU 上使用的浮点数的格式,并将其分解为这些部分。最常见的格式是 IEEE-754。
或者,您可以使用一些特殊函数(double frexp(double value, int *exp);
和 double ldexp(double x, int exp);
)来获取这些部分,如 this answer 所示。
Another option 是将 %a
与 printf()
一起使用。
dtoa
,它在某种程度上是 printf
的子集...
您&
错位了。我想你想要:
s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;
请记住,当您 &
时,您会将未设置的位清零。因此,在这种情况下,您希望在获得指数时将符号位归零,并在获得尾数时将符号位和指数归零。
请注意,面具直接来自您的图片。因此,指数掩码将如下所示:
0 11111111 00000000000000000000000
尾数掩码看起来像:
0 00000000 11111111111111111111111
m |= 0x00800000;
。请注意,应首先检查数字的特殊值(非正规、NaN、无穷大),因为这些需要不同的处理。
在 Linux 包 glibc-headers 上提供带有浮点类型定义的标头 #include <ieee754.h>
,例如:
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. */
std::isnan
开始。
不要制作做多种事情的函数。不要掩饰然后转移;移位然后掩码。不要不必要地改变值,因为它很慢,缓存破坏并且容易出错。不要使用幻数。
/* NaNs, infinities, denormals unhandled */
/* assumes sizeof(float) == 4 and uses ieee754 binary32 format */
/* assumes two's-complement machine */
/* C99 */
#include <stdint.h>
#define SIGN(f) (((f) <= -0.0) ? 1 : 0)
#define AS_U32(f) (*(const uint32_t*)&(f))
#define FLOAT_EXPONENT_WIDTH 8
#define FLOAT_MANTISSA_WIDTH 23
#define FLOAT_BIAS ((1<<(FLOAT_EXPONENT_WIDTH-1))-1) /* 2^(e-1)-1 */
#define MASK(width) ((1<<(width))-1) /* 2^w - 1 */
#define FLOAT_IMPLICIT_MANTISSA_BIT (1<<FLOAT_MANTISSA_WIDTH)
/* correct exponent with bias removed */
int float_exponent(float f) {
return (int)((AS_U32(f) >> FLOAT_MANTISSA_WIDTH) & MASK(FLOAT_EXPONENT_WIDTH)) - FLOAT_BIAS;
}
/* of non-zero, normal floats only */
int float_mantissa(float f) {
return (int)(AS_U32(f) & MASK(FLOAT_MANTISSA_BITS)) | FLOAT_IMPLICIT_MANTISSA_BIT;
}
/* Hacker's Delight book is your friend. */
DBL_MANT_DIG
而不是创建自己的版本!
FLT_MANT_DIG
。
请参阅此 IEEE_754_types.h
标头以了解要提取的联合类型:float
、double
和 long double
,(已处理字节序)。这是一个摘录:
/*
** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** 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
值转换为字符串形式的演示,请参阅 dtoa_base.c
。
此外,请查看 C/CPP Reference Book 的 1.2.1.1.4.2 - Floating-Point Type Memory Layout 部分,它非常好且简单地解释了所有浮点类型的内存表示/布局以及如何按照实际 IEEE 754 Floating 对它们进行解码(带插图) -点规范。
它还具有指向非常好的资源的链接,可以更深入地解释。
float
不是 IEEE 754 32 位二进制(并不罕见)2)unsigned
是 16 位(在嵌入式世界中很常见)3)unsigned/float
的字节序不匹配时,此方法会失败。 (稀有的)。 4)exponent/mantissa
使用数学解释,因为这个答案显示了有偏的指数和不完整的有效数/尾数。union
也不是更好,因为它根本不能保证工作。它当然不是便携式的。没有什么能限制 C 实现对位域进行布局,以便联合将它们映射到float
表示的所需部分,尽管存在依赖类型双关的单独问题。