我想将float的最小可能值添加到float中。因此,例如,我尝试这样做以获得1.0 +可能的最小浮点数:
float result = 1.0f + std::numeric_limits<float>::min();
但在这之后,我得到以下结果:
(result > 1.0f) == false
(result == 1.0f) == true
我正在使用Visual Studio 2015.为什么会这样?我该怎么做才能解决它?
答案 0 :(得分:86)
如果您希望在1之后显示下一个可表示的值,则<cmath>
标题中有一个名为std::nextafter
的函数。
float result = std::nextafter(1.0f, 2.0f);
它从第二个参数方向的第一个参数开始返回下一个可表示的值。因此,如果您想要找到低于1的下一个值,您可以这样做:
float result = std::nextafter(1.0f, 0.0f);
将最小的正可表示值添加到1不起作用,因为1和下一个可表示值之间的差值大于0和下一个可表示值之间的差值。
答案 1 :(得分:41)
您正在观察的“问题”是因为浮点运算的非常本质。
在FP中,精度取决于比例;在值1.0
附近,精度不足以区分1.0
和1.0+min_representable
,其中min_representable
是大于零的最小可能值(即使我们只考虑最小的归一化数,std::numeric_limits<float>::min()
...最小的非正规数是另外几个数量级的小数。)
例如,对于双精度64位IEEE754浮点数,在x=10000000000000000
(10 16 )的范围内,无法区分x
和{{ 1}}。
分辨率随着比例变化的事实是名称“浮点”的原因,因为小数点“浮动”。相反,定点表示将具有固定的分辨率(例如,在单位以下具有16位二进制数字,精度为1/65536~0.00001)。
例如,在IEEE754 32位浮点格式中,符号有一位,指数有8位,尾数有31位:
最小值x+1
,eps
可用作预定义常量1.0f + eps != 1.0f
或std::numeric_limits<float>::epsilon
。另请参阅machine epsilon on Wikipedia,其中讨论了epsilon如何与舍入错误相关。
即。 epsilon是执行此处所期望的最小值,在添加到1.0时会有所不同。
更常见的版本(对于1.0以外的数字)在最后一个位置(尾数)称为1个单位。请参阅维基百科的ULP article。
答案 2 :(得分:20)
min
是(标准化形式)浮点数可以假设的最小非零值,即大约2 -126 (-126是浮点数的最小允许指数) );现在,如果你把它加到1,你仍然会得到1,因为float
只有23位的尾数,所以这么小的变化不能用这么大的数字表示(你需要126比特尾数,看到一个变化,将2 -126 加到1)。
最小可能的更改为1,而不是epsilon
(所谓的机器epsilon),实际上是2 -23 - 因为它会影响最后一位尾数。
答案 3 :(得分:4)
要以尽可能小的数量增加/减少浮点值,请使用nextafter
朝+/- infinity()
。
如果您只使用next_after(x,std::numeric_limits::max())
,则x
无效的结果是错误的。
#include <iostream>
#include <limits>
#include <cmath>
template<typename T>
T next_above(const T& v){
return std::nextafter(v,std::numeric_limits<T>::infinity()) ;
}
template<typename T>
T next_below(const T& v){
return std::nextafter(v,-std::numeric_limits<T>::infinity()) ;
}
int main(){
std::cout << "eps : "<<std::numeric_limits<double>::epsilon()<< std::endl; // gives eps
std::cout << "after : "<<next_above(1.0) - 1.0<< std::endl; // gives eps (the definition of eps)
std::cout << "below : "<<next_below(1.0) - 1.0<< std::endl; // gives -eps/2
// Note: this is what next_above does:
std::cout << std::nextafter(std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::infinity()) << std::endl; // gives inf
// while this is probably not what you need:
std::cout << std::nextafter(std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::max()) << std::endl; // gives 1.79769e+308
}