在C ++中考虑“正常”实数TREAL x
(不是低于正常而不是NaN /无限)(TREAL
= float
,double
,long double
)
以下是从浮点角度查找上一个和下一个x
的好方法吗?
TREAL xprev = (((TREAL)(1.)) - std::numeric_limits<TREAL>::epsilon()) * x;
TREAL xnext = (((TREAL)(1.)) + std::numeric_limits<TREAL>::epsilon()) * x;
非常感谢。
答案 0 :(得分:11)
C99和C ++ 11在<math.h>
和<cmath>
中具有nextafter,nextafterl和nextafterf函数。使用基本算法和epsilon实现它们将是乏味的,因为您需要考虑四舍五入。处理二进制表示可能更容易,但我想知道符号和幅度表示的效果以及-0.0的存在(参见Fred's answer所需的内容)。
答案 1 :(得分:5)
在二进制级别上获取下一个浮点数要容易得多:
float next(float f)
{
unsigned x;
memcpy(&x, &f, 4);
++x;
memcpy(&f, &x, 4);
return f;
}
当然,这只适用于“按升序”存储浮点数的系统,这恰好是IEEE754的情况。
负数将转向负无穷大。希望他们改为零?使用此:
float next(float f)
{
int x;
memcpy(&x, &f, 4);
x += x >> 31 | 1; // this will add 1 for positive f and -1 for negative f
memcpy(&f, &x, 4);
return f;
}
答案 2 :(得分:1)
不,“连续”浮点值之间的比率不均匀;这种方法可能会遗漏一些,或者让你陷入xnext == x
。
要从一个值移动到下一个最大值,您必须:
细节非常繁琐,可能需要一些浮点表示的知识。
但是,假设表示类似于IEEE,您可以通过将位模式重新解释为足够大的整数并递增该整数来实现此目的。这将增加尾数,任何溢出进入指数,就像我们想要的那样。
答案 3 :(得分:0)
以下nextFloat函数为至少为minV的所有浮点数提供正确的结果(即,nextFloat中的中间值不在非正规范围内的浮点数)。我测试了从minV开始的所有浮点数,直到FLT_MAX,结果总是等于nextFloatRef的结果。
float nextFloatRef(float v)
{
uint32_t vBits = reinterpret_cast<uint32_t&>(v);
vBits++;
return reinterpret_cast<float&>(vBits);
}
float nextFloat(float v)
{
return v + v * nextFloatRef(FLT_EPSILON / 2);
}
float minV = FLT_MIN / (FLT_EPSILON / 2);
nextFloatRef(FLT_EPSILON / 2)是一个常数,因此可以预先计算。