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

Squ*_*idy 59 c++ floating-point c++11

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

float result = 1.0f + std::numeric_limits<float>::min();
Run Code Online (Sandbox Code Playgroud)

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

(result > 1.0f) == false
(result == 1.0f) == true
Run Code Online (Sandbox Code Playgroud)

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

Ben*_*ley 86

如果你希望在1之后有下一个可表示的值,那么std::nextafter<cmath>标题中有一个被调用的函数.

float result = std::nextafter(1.0f, 2.0f);
Run Code Online (Sandbox Code Playgroud)

它从第二个参数方向的第一个参数开始返回下一个可表示的值.因此,如果您想要找到低于1的下一个值,您可以这样做:

float result = std::nextafter(1.0f, 0.0f);
Run Code Online (Sandbox Code Playgroud)

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

  • `std :: numeric_limits <float> :: min()`不是最小的正可表示值; 它是最小的正归一化值,因此次正规值可以更低. (18认同)
  • 在IIRC中,大约一半的浮点位模式表示幅度小于"1.0"的数字.指数字段的范围或多或少以"0"为中心(代表尾数的"2 ^ 0 = 1.0"乘数),在考虑其编码方式的偏差后,将FP位模式排序为整数实际工作.请参阅Bruce Dawson关于浮点奇怪内容的精彩系列文章,包括[关于表示的这一篇](https://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format /) (2认同)

650*_*502 41

你观察到的"问题"是因为浮点运算的本质.

在FP中,精度取决于比例; 围绕价值1.0的精度是不够的,能够区分1.01.0+min_representable在那里min_representable可能是最小的值大于零(即使我们只考虑最小规格化数,std::numeric_limits<float>::min()...最小的非正规的幅度要小的另一个几个数量级).

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


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

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

浮点


最小值,eps例如,1.0f + eps != 1.0f可用作预定义常量FLT_EPSILON,或std::numeric_limits<float>::epsilon.另请参阅维基百科上的机器epsilon,其中讨论了epsilon如何与舍入错误相关.

即epsilon是您在此处所期望的最小值,在添加到1.0时会产生差异.

更常见的版本(对于1.0以外的数字)在最后一个位置(尾数)称为1个单位.请参阅维基百科的ULP文章.

  • 我想问题的根源在于人们使用"浮点"(或只是"浮点")这个词来表示"计算机中的非整数"而不考虑(甚至不知道)实际的浮动性质(即精度取决于规模). (5认同)
  • **`eps`是FLT_MIN**的错误名称.`eps`是`FLOAT_EPSILON`的缩写,它是[加到'1.0`时产​​生差异的最小数字](https://en.wikipedia.org/wiki/Machine_epsilon#Variant_definitions).它是"1.0"的最后一个位置(尾数)的1个单位(参见[ulp](https://en.wikipedia.org/wiki/Unit_in_the_last_place)).您所描述的是epsilon和1 ULP的概念,但问题是"eps =最大可能值大于零". (2认同)

Mat*_*lia 20

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

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

  • `std :: numeric_limits <float> :: min()`是最小的正*标准化*值.次正规可以更低. (2认同)
  • 缺乏次正常甚至更加丑陋.使用次正规值减去两个不相等的数字将总是给出非零的答案.如果没有可用的次正规,它就不会. (2认同)