将最小可能的浮点数添加到浮点数
我想将一个浮点数的最小可能值添加到浮点数。 所以,举个例子,我尝试这样做得到1.0 +最小可能的浮点数:
float result = 1.0f + std::numeric_limits<float>::min();
但是做完之后,我得到以下结果:
(result > 1.0f) == false (result == 1.0f) == true
我正在使用Visual Studio 2015.为什么会发生这种情况? 我能做些什么来解决它?
如果你想要在1之后的下一个可表示值,那么在<cmath>
头文件中有一个叫做std::nextafter
的函数。
float result = std::nextafter(1.0f, 2.0f);
它从第一个参数开始向第二个参数的方向返回下一个可表示的值。 所以如果你想find下面的值1,你可以这样做:
float result = std::nextafter(1.0f, 0.0f);
将最小正表示值添加到1不起作用,因为1和下一个可表示值之间的差值大于0和下一个可表示值之间的差值。
你所观察到的“问题”是因为浮点运算的本质 。
在FP中,精度取决于规模; 在值1.0
附近,精度不足以区分1.0
和1.0+min_representable
,其中min_representable
是大于零的最小可能值(即使我们只考虑最小规格化数字, std::numeric_limits<float>::min()
,最小的denormal是更小的几个数量级)。
例如,对于双精度64位IEEE754浮点数,在x=10000000000000000
(10 16 )的范围内,不可能区分x
和x+1
。
分辨率随标度变化的事实是名称“浮点”的原因,因为小数点“浮动”。 一个固定的点表示代替将有一个固定的分辨率(例如16个二进制数字低于你的单位,精度为1/65536〜0.00001)。
例如在IEEE754 32位浮点格式中,符号有一位,指数有8位,尾数有31位:
1.0f + eps != 1.0f
的最小值eps
可用作FLT_EPSILON
或std::numeric_limits<float>::epsilon
的预定义常量。 另请参见维基百科上的机器epsilon ,其中讨论了epsilon如何与舍入错误相关。
即epsilon是最小的价值,做你在这里期待的,使得添加到1.0时有所作为。
这个更通用的版本(对于1.0以外的数字)在最后(尾数)被称为1个单位。 请参阅Wikipedia的ULP文章 。
min
是(标准化forms)float可以假设的最小非零值,即大约2-126 (-126是浮点的最小允许指数)。 现在,如果你总结为1,你仍然会得到1,因为一个float
只有23位尾数,所以这样一个小的变化不能代表这样一个“大”的数字(你需要一个126位尾数看一个变化总和2 -126到1)。
可能的最小改变为1,而是epsilon
(所谓的机器epsilon),实际上是2 – 23 – 因为它影响到尾数的最后一位。
要以最小的可能值增加/减less浮点值,请使用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(1.0,std::numeric_limits<T>::infinity()) ; } template<typename T> T next_below(const T& v){ return std::nextafter(1.0,-std::numeric_limits<T>::infinity()) ; } int main(){ std::cout << next_below(1.0) - 1.0<< std::endl; // gives eps std::cout << next_above(1.0) - 1.0<< std::endl; // gives ~ -eps/2 // Note: std::cout << std::nextafter(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()) << std::endl; // gives inf std::cout << std::nextafter(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::max()) << std::endl; // gives 1.79769e+308 }