我正在编写一个算法来舍入浮点数。输入将是一个64位IEEE754双类型号,非常接近X.5,其中X是小于32的整数。我想到的第一个解决方案是使用位掩码来掩盖那些最低有效位,因为它们代表2 ^ -n的非常小的分数。(假设指数不大)。
但问题是我应该这样做吗?有没有其他方法可以完成同样的事情?我觉得在浮点上使用位操作是非常有争议的。谢谢!
顺便说一句,我使用的语言是C ++。
编辑: 谢谢大家,感谢您的评论。我很感激!假设我有一个浮点数,可以是1.4999999 ...或21.50000012 ....我想把它舍入到1.5或21.5。我的目标是将任何数字舍入到最接近X.5的形式,因为它可以存储在IEEE754浮点数中。
答案 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)用于浮点。
我认为应该指出的是,有一些功能可以为你处理多种类型的舍入。