找到浮点计数器的最大值

时间:2018-11-07 16:06:03

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

我很抱歉,是否曾经有人问过我,但是我找不到。

我想知道是否有一种方法可以计算出用作计数器的单个精度浮点数将达到“最大值”的点(由于该原因,该点不再能够添加另一个值)导致精度下降。

例如,如果我将0.1f连续添加到float,我最终将到达一个值不变的地方:

const float INCREMENT = 0.1f;
float value = INCREMENT;
float prevVal = 0.0f;

do {
  prevVal = value;
  value += INCREMENT;
} while (value != prevVal);

cout << value << endl;

在GCC上,此输出2.09715e+06

是否有一种方法可以针对INCREMENT的不同值进行数学计算?我认为理论上应该是float的指数部分需要移位超过23位,从而导致尾数丢失并仅加0的情况。

3 个答案:

答案 0 :(得分:2)

给定一个正数y作为增量,最小的X加上y不会产生大于X的结果,这是2的最小幂而不是小于y除以浮点格式的“ε”的一半。可以通过以下方式计算:

Float Y = y*2/std::numeric_limits<Float>::epsilon();
int e;
std::frexp(Y, &e);
Float X = std::ldexp(.5, e);
if (X < Y) X *= 2;

下面是一个证明。我假设IEEE-754二进制浮点算法使用的是从最近到最近的联系。

在IEEE-754浮点算术中添加两个数字时,结果是精确的数学结果,四舍五入到选定方向上最接近的可表示值。

关于符号的注释:source code format中的文本表示浮点值和运算。其他文字是数学的。因此 x + y x y 的精确数学和,x x 浮点格式,并且x+y是在浮点运算中添加xy的结果。另外,我将在C ++中将Float用作浮点类型。

给出一个浮点数 x ,考虑使用浮点算术x+y添加一个正值 y 。在什么条件下结果将超过 x

x 1 为下一个大于浮点格式表示的 x 的值,然后让 x m x x 1 之间的中点。如果 x + y 的数学值小于 x m ,则浮点计算{{ 1}}向下舍入,因此产生 x 。如果 x + y 大于 x m ,则将其四舍五入并生成 x 1 ,或者它产生更大的数字,因为 y 足够大,可以将总和移到 x 1 。如果 x + y 等于 x m ,则结果为 x x 1 的位数是偶数。出于某些原因,在与该问题相关的情况下,该值始终为 x ,因此计算会向下取整。

因此,{且只有当 x + y 超过 x <时,x+y才会产生大于 x 的结果。 / em> m ,表示 y 超过了 x x 1 的距离的一半子>。请注意,从 x x 1 的距离是x+y有效数字的低位数字1。< / p>

在二进制浮点格式中,有效位数为 p 位,低位的位置值为2 1− p 乘以高位的位置值。例如,如果 x 为2 e ,则有效位的最高位表示2 e < / sup>,最低位代表2 e + 1− p

在给定 y 的情况下,问题询问x产生的结果小于 x 的最小 x 是多少? em>?它是 y 不超过x+y有效数字的低位数字一半的最小 x

让2 e x 的有效位的高位的位置值。然后 y ≤½•2 e + 1− p = 2 e - p ,所以 y •2 p ≤2 e

因此,给定正数 y x产生的结果小于 x 的最小 x 具有其前导位2 e ,等于或超过 y •2 p 。实际上,它必须正好是2 e ,因为所有其他浮点数的前导位的位置值是2 e 将其他位设置为有效位,因此它们更大。 2 e 是前导位代表2 e 的最小数字。

因此, x 是两个等于或超过 y •2 p 的最小幂。< / p>

在C ++中,x+y(来自std::numeric_limits<Float>::epsilon()头)是从1到下一个可表示值的步长,表示它是2 1-−em> p < / sup>。因此 y •2 p 等于<limits>。 (除非溢出到∞,否则此操作是精确的。)

让我们将其分配给变量:

y*2/std::numeric_limits<Float>::epsilon()

我们可以使用Float Y = y*2/std::numeric_limits<Float>::epsilon(); (来自frexp标头)从浮点中提取指数,从而找到由 Y 的有效位的最高位表示的位置值。 <cmath>Y(也称为ldexp)的点表示,以将指数应用于新的有效数(<cmath>,因为.5和{{ 1}}使用):

frexp

然后 X 是2的幂,并且小于或等于 Y 。实际上,这是2的最大乘方,不大于 Y ,因为下一个更大的2乘方2, X 大于 Y 。但是,我们想要的最小二乘幂不少于 Y 。我们可以通过以下方式找到它:

ldexp

结果 X 是问题所寻求的数字。

答案 1 :(得分:0)

是的,有可能。 std::numeric_limits::epsilon()定义了可以增加值1.0的最小值。

使用此功能,您可以计算任何数字的限制。

C中有DBL_EPSILON

所以在您的情况下,是这样的:

template<class T>
auto maximumWhenAdding(T delta) -> T
{
    static_assert(std::is_floating_point_v<T>, "Works only for floating points.");
    int power2= std::ilogb(delta);
    float roudedDelta = ldexp(T { 1.0 }, power2);
    if (roudedDelta != delta) {
        roudedDelta *= 2;
    }

    return 2 * roudedDelta / std::numeric_limits<T>::epsilon();
}

live example C++

实时测试示例delta中的注释未能增加maxForDelta,但是减法成功,因此这正是您所需要的。

答案 2 :(得分:0)

Marek's Answer非常接近,并且是使用程序找到它的一种不错的方式(比我最初发布的程序效率更高)。但是,我不一定需要程序形式的答案,而只是数学形式的答案。

据我所知,答案取决于所用增量的指数以及尾数位数。我们需要四舍五入到最接近的2的幂,这有点复杂。基本上,如果尾数为0,则不执行任何操作,否则将指数加1。因此,假设我们现在的增量为2的幂,表示为1.0 x 2exp,尾数为N位,则最大值为1.0 x 2(N + exp)。请注意,C中的FLT_EPSILON等于1.0 x 2-N。因此,我们也可以通过将最接近的2的幂除以FLT_EPSILON来找到它。

对于增量0.1,最接近的2的幂是0.125,即1.0 x 2-3。因此,我们需要1.0 x 2(23 + (-3))1.0 x 221等于2097152