当...给出0x800000时,我的函数返回0 ....正确的答案是0x00400000。
这是除以2的最小正常float
值,并在下面#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)
,该数字不是有限的(它是无穷大或非数字),应该单独留下。
这样的代码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
跟在后面的情况下重写。它是经过测试的代码 - 通过许多组合和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。
可能值得指出的是,通过指数的简单递减,IEEE-754“binary32”值的除以2仅适用于规范化的浮点数。额外的缩放对于正确处理非正常(子正常)是必要的。 – njuffa
采取点,@ njuffa。我已经对我的评论进行了限定,以澄清它们适用于规范化投入。 –