当 ... 给定 0x800000 时,我的函数返回 0 ....,正确答案 ... 是 0x00400000。
这是将最小 正常 float 值除以 2,在下面的 #3 中有详细说明。
代码有很多问题。
对于大多数有限数,递减而不是移动指数是正确的,正如 @John Bollinger 好的答案所指出的那样当指数大于 1 时。
当exponent == 0 时,数字为sub-normal(或非正规),需要将其mantissa 字段右移(/2)。指数保持为 0。如果移出的位为 1,则除以 2 不准确。然后,根据四舍五入,mantissa 被调整 - 可能是通过添加 1。
当exponent == 1时,结果将是次正常的,需要在mantissa字段中创建隐含的正常数字位并右移(/2)。如上所述,这种转变可能会导致四舍五入。指数变为 0。请注意,“舍入”mant 可能会超过 mant 的最大值 0x7FFFFF,然后需要对字段进行调整。
当exponent == MAX (255) 时,数字不是有限的(它是无穷大或Not-a-Number),应该单独保留。
-
1 << 31 之类的代码最好定义为:
// unsigned signBit = (1 << 31) & uf;
unsigned signBit = (1u << 31) & uf; // Use an unsigned mask
unsigned signBit = (1LU << 31) & uf; // unsigned may be 16 bit.
// or better yet
unsigned signBit = uf & 0x80000000;
-
mantissa 派生的角落弱点在于它依赖于(非常常见的)2 的补码。便携式替代品:
// unsigned mantissa = ~0; Incorrect mask in `mantissa` when `int` is not 2's comp.
// unsigned mantissa = -1; correct all bits set.
// mantissa >>= 9;
// mantissa &= uf;
// or simply use
unsigned mantissa = 0x7FFFFF & uf;
unsigned 可能是 16、32、64 位等。最好使用最小或精确宽度类型。
#define SIGN_MASK 0x80000000
#define EXPO_MASK 0x7F800000
#define MANT_MASK 0x007FFFFF
#define EXPO_SHIFT 23
#define EXPO_MAX (EXPO_MASK >> EXPO_SHIFT)
#define MANT_IMPLIED_BIT (MANT_MASK + 1u)
uint32_t divideFloatBy2(uint32_t uf){
unsigned sign = uf & SIGN_MASK;
unsigned expo = uf & EXPO_MASK;
unsigned mant = uf & MANT_MASK;
expo >>= EXPO_SHIFT;
// when the number is not an infinity nor NaN
if (expo != EXPO_MAX) {
if (expo > 1) {
expo--; // this is the usual case
} else {
if (expo == 1) {
mant |= MANT_IMPLIED_BIT;
}
expo = 0;
unsigned round_bit = mant & 1;
mant /= 2;
if (round_bit) {
TBD_CODE_Handle_Rounding(round_mode, sign, &expo, &mant);
}
}
expo <<= EXPO_SHIFT;
uf = sign | expo | mant;
}
return uf;
}
OP 后来评论了exponent ,sign 0, mantissa == 0x3, expected result is 0x2, but my returning 1.,所以舍入模式可能是FE_TONEAREST 或FE_UPWARD。
在expo <= 1 跟随时重写案例。它是经过测试的代码 - 经历了许多 232 组合和 4 种舍入模式。
请注意,当some_float/2.0f 计算时,它可能会影响浮点环境 状态位。我最初也是这样做的,但后来从这篇文章中删除了该代码 - 如果有兴趣,请联系。
} else {
if (expo == 1) {
expo = 0;
mant |= MANT_IMPLIED_BIT;
}
// Divided by 2 result inexact?
if (mant % 2) {
mant /= 2;
// Determine how to round
switch (fegetround()) {
case FE_DOWNWARD:
if (sign) mant++;
break;
case FE_TOWARDZERO:
break;
case FE_UPWARD:
if (!sign) mant++;
break;
default: // When mode is not known, act like FE_TONEAREST
// fall through
case FE_TONEAREST:
if (mant & 1) mant++;
break;
}
if (mant >= MANT_IMPLIED_BIT) {
mant = 0;
expo++;
}
} else {
mant /= 2;
}
}
有关舍入模式的详细信息,请搜索FE_... 宏或here。