为了在不允许重复的数据结构(如二叉树、堆等)中对浮点数进行排序,我想将
float
舍入到一定程度的精度,如下所示:
float round_to_epsilon(x);
bool less_than(float x, float y) {
return round_to_epsilon(x) < round_to_epsilon(y);
}
换句话说,我想将
float
量化为std::numeric_limits<float>::epsilon
的倍数,即2-23。
乍一看,您似乎可以这样做:
float round_to_epsilon(float x) {
// note: floor or ceil must be used to avoid bias around zero
return std::floor(x / std::numeric_limits<float>::epsilon());
}
但是,这会将每个大于 2105 的数字变成正无穷大,这不是溢出正确的。
另一种方法是乘以一个非常小的数字来故意导致下溢,并丢弃一些数字。然而,这使得非规范化的数字很可能出现,因此这种方法可能会出现严重的性能问题。
如何舍入为 epsilon;或者换句话说,如何将数字降到 2-23 以下,而不导致不必要的上溢或下溢?
我的直觉是,也许我可以除以二的幂,然后再次相乘以获得避免非规范化的数字,但我不确定如何做到这一点,或者这是否是一个好方法。
换句话说,我想将
量化为float
的倍数std::numeric_limits<float>::epsilon
我将按要求回答问题,而不讨论这是否是最适合所述用例的方法。您的第一个想法实际上是可行的,只要处理输入域的末端即可。
数量级足够大的数字自然会被量化为
eps
的整数倍。比 eps
小得多的数字将四舍五入为零。要在量化过程中舍入到最接近或偶数,可以使用 rint()
,前提是默认舍入模式设置为舍入到最近或偶数(通常是这样)。此警告是必要的,因为与 floor()
、
ceil()
和
trunc()
不同,
rint()
不使用固定舍入模式运行,而是使用当前有效的舍入模式。
float round_to_epsilon (float x)
{
float eps, cutoff, r;
eps = std::numeric_limits<float>::epsilon();
cutoff = 0.5f * eps;
if (x < cutoff) {
r = copysignf (0.0f, x);
} else if (x >= 2.0f) {
r = x;
} else {
r = eps * rintf (x / eps);
}
return r;
}