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

时间:2016-12-24 23:35:09

标签: c++ c++11 floating-point

我想将float的最小可能值添加到float中。因此,例如,我尝试这样做以获得1.0 +可能的最小浮点数:

float result = 1.0f + std::numeric_limits<float>::min();

但在这之后,我得到以下结果:

(result > 1.0f) == false
(result == 1.0f) == true

我正在使用Visual Studio 2015.为什么会这样?我该怎么做才能解决它?

4 个答案:

答案 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.01.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位:

floating point

最小值x+1eps可用作预定义常量1.0f + eps != 1.0fstd::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

}