将最小可能的浮点数添加到浮点数

我想将一个浮点数的最小可能值添加到浮点数。 所以,举个例子,我尝试这样做得到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.01.0+min_representable ,其中min_representable是大于零的最小可能值(即使我们只考虑最小规格化数字, std::numeric_limits<float>::min() ,最小的denormal是更小的几个数量级)。

例如,对于双精度64位IEEE754浮点数,在x=10000000000000000 (10 16 )的范围内,不可能区分xx+1


分辨率随标度变化的事实是名称“浮点”的原因,因为小数点“浮动”。 一个固定的点表示代替将有一个固定的分辨率(例如16个二进制数字低于你的单位,精度为1/65536〜0.00001)。

例如在IEEE754 32位浮点格式中,符号有一位,指数有8位,尾数有31位:

浮点


1.0f + eps != 1.0f的最小值eps可用作FLT_EPSILONstd::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 }