C / C ++:浮点运算问题

时间:2011-01-15 01:30:09

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

如果浮动重复乘以小于1的数字,浮点数可能变为零是否合理?

以下是一个例子:

float number = 1.0f;

for ( int i = 0; i < ONE_BILLION; ++i )
{
    number *= 0.01f;
}

但请不要限制你对这个例子的回答。

谢谢!

6 个答案:

答案 0 :(得分:5)

是的,当乘法的结果小于最接近零的可表示数时,它将变为零。对于IEEE浮点,任何小于或等于0.5f(但大于零)的乘数都会发生这种情况;但是,如果乘数甚至略大于0.5f(例如,0.5f + FLT_EPSILON),则结果将收敛到最小的可表示正数并永远保持在那里。比较使用和不使用-DGREATER的此程序的行为:

#include <stdio.h>
#include <float.h>
#ifdef GREATER
#define MULTIPLIER (0.5f + FLT_EPSILON)
#else
#define MULTIPLIER 0.5f
#endif
int
main(void)
{
  float x = 1.0f;
  unsigned int count = 0;
  while (x > 0.0f && count < 200)
  {
    x *= MULTIPLIER;
    printf("%g %a\n", x, x);
    count++;
  }
  return 0;
}

答案 1 :(得分:3)

取决于实施。使用典型的IEEE 754实现,您将首先进入非正规(丢失精度)然后捕捉到0.但是其他一些实现可能会给您一个浮点下溢错误,爆炸崩溃。

干杯&amp;第h。,

答案 2 :(得分:2)

是。最终你会得到一个小于最小可表示正浮点数的数字,它将下溢到零。

答案 3 :(得分:2)

取决于乘法值和当前舍入模式。考虑这个例子:

FLT_MIN * 0.99 == FLT_MIN 

编辑: @Pascal:在我的电脑上,这个用GCC编译的简单示例:

int main(){
    float f = 1.0f;
    while (f){
        printf("%g\n", f);
        f *= 0.99f;
    };
    return 0;
}

收敛到6.93643e-044,而不是零。

我的观点是,如果以比最终结果更精确的方式执行中间计算(最常见的例子是x87 80位内部FP寄存器),并且最后结果被截断为float,那么你肯定会得到零。但是如果计算是在浮点数上进行的,那么你就没有任何保证它们会收敛到零。 Here是一个很好的阅读。

对于一般情况,最好的(也是更快)解决方案是将循环转换为具有对数的简单表达式:

#include <math.h>
#define COUNT 1000
#define COEFF 0.9f

int main(){
    float f = 1.0f;
    float f2 = exp(log(f) + COUNT * log(COEFF));
    int i;
    for (i = 0; i < COUNT; i++)
        f *= COEFF;
    printf("results for %d iters with %f: LOOP: %g and LOG: %g\n", COUNT, COEFF, f, f2);
    return 0;
};

将输出:

results for 1000 iters with 0.900000: LOOP: 5.04467e-045 and LOG: 0

答案 4 :(得分:1)

我很惊讶,很少有人指出这样一个简单的事实:当你低于/高于最小/最大可表示(绝对)非零/非无限值时,你将得到一个下溢/溢出异常,这将停止执行。这是因为在数学上不正确的是,非零数字(例如)乘以另一个非零数字可能会变成零,就像通过传递比例中的某个点而变为无穷大一样。

当然,您可以屏蔽FP控制字中的异常并接受您的程序在数学上的行为不正确并最终传递零,您可能将其插入一个分区并获得另一个异常。

在我看来,控制FP处理要好得多。我遇到过这样的组织,他们已经为fp留下了整数数学来“简化编程”,但最终得到了一个他们只是模糊地理解的处理背景和所谓的“奇怪”结果和例外,他们绝对不会这样做。

答案 5 :(得分:0)

@Jay

如果您想了解浮点数,请阅读thisthis