对于类项目,我试图从IEEE浮点数中提取3位字段,将其乘以0.5,然后重新构建数字。我通过将它们推入无符号整数来完成提取工作:
x = 5.5
x: 1000000101100000000000000000000 (1085276160, 0x40b00000)
extracted sign: 0 (0, 0x0)
extracted exp: 10000001 (129, 0x81)
extracted sig: 01100000000000000000000 (1610612736, 0x60000000)
我的两个问题是:
a)如何将这些位重新组成原始输入?我试过了:
return sign ^ exp ^ sig
并没有得到正确的结果。
b)如何将数字乘以0.5?我是否乘以sig或exp?
答案 0 :(得分:0)
a)你可以“但是这些位回来了”与使用ands(掩码)和ors(将它们重新组合在一起)的组合一起使用。使用程序员的计算器应该足以理解它是如何工作的。
例如(伪-C):
unsigned int sign = ...;
unsigned int exp = ...;
unsigned int sig = ...;
请参阅:float format
unsigned int out;
out = ((sign & 1)<<31)|((exp & 0xff)<<23)|(sig & 0x7fffff);
&amp;的重要性1,&amp; 0xff和&amp; 0x07fffff是它们各自分别设置了1位,8位和23位。
b)要么工作,要么将指数移动+ -1,则乘以/除以2。这不会降低“精度”,因为有效数字保持不变。如果指数已经处于最小值,那么唯一的方法就是开始减少signifcand,这将是非规范化。
注意:对于未存储的有效数字,存在隐含的高阶1位。换句话说,除非指数为零,否则存在第24位未存储且被假定为1位。
答案 1 :(得分:0)
a)如何将这些位重新组成原始输入?
要重新构建float
的32位表示形式,请使用|
,而不是^
。
#include <stdint.h>
#define FLT_SIGN_SFT 31
#define FLT_EXP_SFT 23
#define FLT_SIG_SFT 0
#define FLT_SIGN_MSK 1
#define FLT_EXP_MSK 0xFF
#define FLT_SIG_MSK 0x7FFFFF
#define FLT_SIG_IMPLIED_BIT (0x7FFFFF + 1)
float float_form(uint32_t sign, uint32_t exp, uint32_t sig) {
union {
uint32_t u32;
float f;
} u;
u.u32 = sign << FLT_SIGN_SFT | exp << FLT_EXP_SFT | sig << FLT_SIG_SFT;
return u.f;
}
Endian关注:只要float
和uint32_t
都是相同的endian,就可以了。需要进行其他调整。
如何将数字乘以0.5?我是否乘以sig或exp?
这很棘手。如果x
是NaN或Infinty,则不执行任何操作。如果x
在正常数字中,则递减指数(特殊情况:如果值现在是次正常)。如果x
是次正常,则移位有效数字。如果1位被移出,请考虑舍入。如果舍入导致指数变化,请调整。
float float_div2(float f) {
union {
uint32_t u32;
float f;
} u;
u.f = f;
uint32_t sign = (u.u32 >> FLT_SIGN_SFT) & FLT_SIGN_MSK;
uint32_t exp = (u.u32 >> FLT_EXP_SFT) & FLT_EXP_MSK;
uint32_t sig = (u.u32 >> FLT_SIG_SFT) & FLT_SIG_MSK;
if (exp < FLT_EXP_MSK) {
unsigned shift_out = 0;
if (exp > 0) {
exp--;
if (exp == 0) {
sig += FLT_SIG_IMPLIED_BIT;
shift_out = sig % 2u;
sig /= 2;
}
} else {
shift_out = sig % 2u;
sig /= 2;
}
if (shift_out > 0) {
assert(exp == 0);
// Assume round to even
if (sig % 2) {
sig++;
if (sig >= FLT_SIG_IMPLIED_BIT) {
sig -= FLT_SIG_IMPLIED_BIT;
exp++;
}
} // end if (sig % 2)
} // end if (exp > 0)
} // end if (exp < FLT_EXP_MSK)
return float_form(sign, exp, sig);
}
测试代码。针对所有float
成功测试。
void float_div2_test(float x) {
float y = x / 2.0f;
float z = float_div2(x);
if (memcmp(&y, &z, sizeof z)) {
printf("%.10e %.10e %.10e\n", x, y, z);
printf("%a %a %a\n", x, y, z);
exit(1);
}
}
void float_div2_tests() {
union {
uint32_t u32;
float f;
} u;
u.u32 = 0;
do {
u.u32--;
float_div2_test(u.f);
} while (u.u32);
puts("Success!");
}