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;
}
字节序问题:只要float 和uint32_t 具有相同的字节序,就可以。需要进行其他调整。
如何将数字乘以 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!");
}