我应该对浮点数使用位操作吗?

时间:2014-01-12 14:08:10

标签: c++ floating-point bit-manipulation

我正在编写一个算法来舍入浮点数。输入将是一个64位IEEE754双类型号,非常接近X.5,其中X是小于32的整数。我想到的第一个解决方案是使用位掩码来掩盖那些最低有效位,因为它们代表2 ^ -n的非常小的分数。(假设指数不大)。

但问题是我应该这样做吗?有没有其他方法可以完成同样的事情?我觉得在浮点上使用位操作是非常有争议的。谢谢!

顺便说一句,我使用的语言是C ++。

编辑: 谢谢大家,感谢您的评论。我很感激!假设我有一个浮点数,可以是1.4999999 ...或21.50000012 ....我想把它舍入到1.5或21.5。我的目标是将任何数字舍入到最接近X.5的形式,因为它可以存储在IEEE754浮点数中。

5 个答案:

答案 0 :(得分:6)

如果您的编译器保证您使用的是IEEE 754浮点数,我建议您根据this blog post中描述的方法进行舍入:add,然后立即减去一个大常量以便发送值在ULP为0.5的浮点数的二分之一处。您将找不到任何更快的方法,并且它不涉及任何位操作。

将0到32之间的数字舍入到IEEE 754双精度最近的停止单位的适当常量是2251799813685248.0

摘要:使用x = x + 2251799813685248.0 - 2251799813685248.0;

答案 1 :(得分:5)

您可以使用round()floor()ceil()rint()nearbyint()trunc()中的任何一种功能。所有都在不同模式下进行四舍五入,并且都是标准C99。您唯一需要做的就是通过将-lm指定为编译器标志来链接标准数学库。

至于尝试通过位操作实现舍入,我会远离它:a)它将比使用上述函数慢得多(它们通常在可能的情况下使用硬件设施),b)它正在重新发明轮子很多潜在的bug,以及c)较新的C标准不喜欢你对浮点类型进行一些操作:它们使用所谓的严格别名规则,不允许你只将double*转换为{ {1}}。您可能需要通过转换为uint64_t*并逐字节操作IEEE数字来进行位操作,或者您必须使用unsigned char*memcpy()复制位表示变量为double并再次返回。对于已经以标准化功能和硬件支持形式提供的东西,很多麻烦。

答案 2 :(得分:3)

您希望将x舍入到最接近的d.5形式的值。对于您编写的通用编号:

round(x+0.5)-0.5

对于接近d.5且距离不到0.25的数字,您可以使用Pascal的产品:

round(2*x)*0.5

答案 3 :(得分:1)

如果您正在寻找一些技巧并且保证在您描述的范围内有双打,那么您可以执行类似这样的操作(按您认为合适的内联方式):

void RoundNearestHalf(double &d) {
    unsigned const          maskshift  = ((*(unsigned __int64*)&d >> 52) - 1023);
    unsigned __int64 const  setmask    =  0x0008000000000000 >> maskshift;
    unsigned __int64 const  clearmask  = ~0x0007FFFFFFFFFFFF >> maskshift;
    *(unsigned __int64*)&d            |= setmask;
    *(unsigned __int64*)&d            &= clearmask;
}

maskshift是无偏的指数。对于输入范围,我们知道这将是非负的并且不超过4(该技巧也适用于更高的值,但不超过51)。我们使用这个值来设置一个setmask来设置尾数中的2 ^ -1(一半)位置,clearmask清除尾数中低于2 ^ -1的所有位。结果是d四舍五入到最接近的一半。

请注意,对于其他实现,可能需要使用标准库来确定其实际上是否更快。

答案 4 :(得分:0)

我无法肯定地谈论C ++,但在C99中,使用IEEE 754标准的浮点将纯粹是规范性的(不是必需的)。在C99中,如果设置了__STDC_IEC_559__宏,则它声明IEC 559(或多或少是IEEE 754)用于浮点。

我认为应该指出的是,有一些功能可以为你处理多种类型的舍入。