C++ 将可能最小的浮点添加到浮点

C++ 将可能最小的浮点添加到浮点,c++,c++11,floating-point,C++,C++11,Floating Point,我想将浮点的最小可能值添加到浮点中。例如,我尝试这样做是为了得到1.0+最小可能的浮点: float result = 1.0f + std::numeric_limits<float>::min(); 我正在使用Visual Studio 2015。为什么会发生这种情况?我能做些什么来绕过它呢?如果您想要1之后的下一个可表示值,则从标题调用该值的函数 float result = std::nextafter(1.0f, 2.0f); 它返回第二个参数方向上从第一个参数开始的下

我想将浮点的最小可能值添加到浮点中。例如,我尝试这样做是为了得到1.0+最小可能的浮点:

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

我正在使用Visual Studio 2015。为什么会发生这种情况?我能做些什么来绕过它呢?

如果您想要1之后的下一个可表示值,则从
标题调用该值的函数

float result = std::nextafter(1.0f, 2.0f);
它返回第二个参数方向上从第一个参数开始的下一个可表示值。因此,如果要找到1以下的下一个值,可以执行以下操作:

float result = std::nextafter(1.0f, 0.0f);

将最小的正表示值添加到1不起作用,因为1和下一个表示值之间的差值大于0和下一个表示值之间的差值。

min
是(标准化形式)浮点可以假定的最小非零值,即2-126左右的值(-126是浮点允许的最小指数);现在,如果你把它和1相加,你仍然会得到1,因为
float
只有23位尾数,所以这么小的变化不能用这么大的数字来表示(你需要126位尾数才能看到2-126和1的变化)

改为1的最小可能变化是
epsilon
(所谓的机器epsilon),实际上是2-23,因为它影响尾数的最后一位。

您观察到的“问题”是因为浮点运算的本质

在FP中,精度取决于刻度;在值
1.0
周围,精度不足以区分
1.0
1.0+min\u可表示
,其中
min\u可表示
是可能大于零的最小值(即使我们只考虑最小归一化数,<代码> STD::NoimiCixLim::(…)/代码>……最小的反常是更小的几个数量级)。< /P> 例如,对于双精度64位IEEE754浮点数,在
x=100000000000000
(1016)的范围内,无法区分
x
x+1


分辨率随比例变化这一事实正是“浮点”这个名称的原因,因为小数点是“浮动的”。相反,定点表示将具有固定的分辨率(例如,单位以下16位二进制数字的精度为1/65536~0.00001)

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


最小值
eps
,使得
1.0f+eps!=1.0f
作为预定义常量可用,如
FLT_EPSILON
,或。另请参见,其中讨论了EPSILON与舍入误差的关系

也就是说,epsilon是最小的值,它实现了您在这里所期望的,当添加到1.0时会产生差异


更一般的版本(对于1.0以外的数字)称为尾数最后一位的1个单位。参见维基百科。

要将浮点值增加/减少尽可能小的数量,请使用
nextafter
朝向+/-
无穷大()

如果只在(x,std::numeric\u limits::max())之后使用
next\u
,则在
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

}
#包括
#包括
#包括
模板
上一步(施工T&v){
返回std::nextafter(v,std::numeric_limits::infinity());
}
模板
下一个T_(施工T&v){
返回std::nextafter(v,-std::numeric_limits::infinity());
}
int main(){

std::难道你为什么感到惊讶吗?你在加min,而不是epsilon。我不知道有什么区别!我假设它们总是相等的。谢谢,这很有帮助。@Matteo回答?我对这个问题没有确切的理由。值得一读的是:
float
的最小正值比它小38个数量级小于1,在1.175e-38处。
float
类型仅提供六位精度,因此将最小值添加到1等于添加零。
std::numeric\u limits::min()
是最小的正规范化值。次规范值可以更低。@user2357112:我应该在我的配置文件中添加一条警告“我所做的任何关于浮点的讨论都忽略了非规范化数字,这是最好忽略的”:-)缺少次正常值就更难看了。有次正常值可用时,减去两个不相等的数字总是会得到一个非零的答案。没有次正常值就不会了。@plugwash:huh,这很好。即使输入已经是非正常的,这也是正确的,因为这只是尾数上的整数数学。
std::numeric_limits::min()
不是最小的正表示值;它是最小的正规范化值,因此次规范化值可以更低。IIRC,大约一半的浮点位模式表示一个大小小于
1.0
的数字。指数字段的范围或多或少集中在
0
(表示尾数的
2^0=1.0
乘数),在考虑了编码方式的偏差后,这使得将FP位模式作为整数进行排序实际上是可行的。请参阅Bruce Dawson关于浮点奇怪东西的优秀系列文章,包括该系列FP文章中的目录。此外,+/-Infinity对于
std::nextafter
,也是一个很好的第二个参数你肯定想朝一个方向或另一个方向走。这可能会更快,这取决于实现如何检查+/-0.0周围的特殊情况。我想问题的根源在于人们在不考虑(甚至不知道)的情况下,将“浮点”(或仅仅是“浮点”)一词用于“计算机中的非整数”实际浮动性质(即精度取决于刻度)。正确。如果
#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

}