Разные результаты к одной и той же сумме (C язык) (проблема округления) - PullRequest
0 голосов
/ 09 марта 2020

Ну, я пытаюсь суммировать вектор, используя два разных метода. Результаты должны быть одинаковыми, но этого не происходит.

Метод 1: вектор суммируется линейным образом;

Метод 2: вектор суммируется в двух частях;

Код представлен ниже:

#include <stdio.h>
#include <stdlib.h>

int main(void){
    int i,n;
    double a,b,c;
    a=0;
    b=0;
    c=0;
    n=1000000;

//Initialization
    double vec[n];
    for(i=0;i<n;i++)
    {
        vec[i]=1.0001;
    }

//Sum Method 1
    for(i=0;i<n;i++)
    {
        a+=vec[i];
    }

//Sum Method 2
    for(i=0;i<n/2;i++)
    {
        b+=vec[i];
    }
    for(i=n/2;i<n;i++)
    {
        c+=vec[i];
    }
    b=c+b;

    //------------------------------------ 
    printf("Method 1: %.30f\n",a);
    printf("Method 2: %.30f\n",b);
    return 0;
}

Вывод:

Method 1: 1000099.999976676190271973609924316406
Method 2: 1000100.000003988854587078094482421875

Есть ли способ решить эту проблему?

Ответы [ 2 ]

1 голос
/ 09 марта 2020

Сложение с плавающей точкой не является ассоциативным. В первом вы складываете как: (((x1 + x2) + x3) + x4). В то время как во втором, как (x1 + x2) + (x3 + x4).

0 голосов
/ 09 марта 2020

В double нет 30 цифр точности, поэтому использование %.30f не слишком важно.

A double имеет дробную часть, которая составляет около 52 двоичных битов. Или около 16 десятичных цифр.

Как уже упоминалось, плавающая точка не точна, и порядок операций может повлиять на результат.

Лучше увидеть, насколько далеки два результата, в процентах от «идеального» (то есть a).

Я добавил это в вашу программу. Результаты:

Method 1: 1000099.9999766761902720
Method 2: 1000100.0000039888545871
dif: -0.0000273126643151
pct: -0.0000000027309933

Таким образом, разница составляет около 2 миллиардов 1 процента.

Но это немного вводит в заблуждение. Обратите внимание, что a и b отключены примерно на 0.00002 , но с целочисленной частью, использующей 7 цифр, это означает около 12 цифр.

Другими словами, два метода [для всех практических целей] эквивалентны в пределах ожидаемой точности и порядка операций.


#include <stdio.h>
#include <stdlib.h>

int
main(void)
{
    int i,
     n;
    double a,
     b,
     c;

    a = 0;
    b = 0;
    c = 0;
    n = 1000000;

//Initialization
    double vec[n];

    for (i = 0; i < n; i++) {
        vec[i] = 1.0001;
    }

//Sum Method 1
    for (i = 0; i < n; i++) {
        a += vec[i];
    }

//Sum Method 2
    for (i = 0; i < n / 2; i++) {
        b += vec[i];
    }
    for (i = n / 2; i < n; i++) {
        c += vec[i];
    }
    b = c + b;

    // ------------------------------------
    printf("Method 1: %.16f\n", a);
    printf("Method 2: %.16f\n", b);

    double dif = a - b;
    printf("dif: %.16f\n", dif);

    double pct = dif / a;
    pct *= 100;
    printf("pct: %.16f\n", pct);

    return 0;
}
...