Member 12942893 Ответов: 2

Работа с очень малыми числами в C++


Ноль вниз голосовать
любимый
Я имею дело с кодом, который использует очень маленькие числа порядка от 10^-15 до 10^-25, я пытался использовать double и long double, но я получаю неправильный ответ, так как либо 0.000000000000000000001 округляется до 0, либо число типа 0.00000000000000002 представляется как 0.00000000000000001999999999999, так как даже небольшая доля 1/1000000 имеет существенное значение в моих окончательных ответах, пожалуйста, предложите мне соответствующее исправление. Спасибо

Что я уже пробовал:

#include <iostream>
     #include<math.h>
     #include<stdlib.h>
     #include<iomanip>
     using namespace std;
     int main()
     {
        double  sum, a, b, c,d;
        a=1;
        b=1*pow(10,-15);
        c=2*pow(10,-14);
        d=3*pow(10,-14);
        sum=a+b+c+d;
        cout<<fixed;
        cout<<setprecision(30);
        cout<<" a   : "<<a<<endl<<" b   : "<<b<<endl<<" c   : "<<c<<endl
            <<" d   : "<<d<<endl; 
        cout<<" sum : "<<sum<<endl<<endl;
        a=a/sum;
        b=b/sum;
        c=c/sum;
        d=d/sum;
        sum=a+b+c+d;
        cout<<" a   : "<<a<<endl<<" b   : "<<b<<endl<<" c   : "<<c<<endl
            <<" d   : "<<d<<endl; 
        cout<<" sum2: "<<sum<< endl;
        return 0;
}


Ожидаемый результат должен быть
ответ : 1.000000000000000000000000000000
б : 0.000000000000001000000000000000
с : 0.000000000000020000000000000000
д : 0.000000000000030000000000000000
сумма: 1.000000000000051000000000000000

ответ : 1.000000000000000000000000000000
б : 0.000000000000001000000000000000
с : 0.000000000000020000000000000000
д : 0.000000000000030000000000000000
sum1: 1.000000000000051000000000000000

Но выход, который я получаю, таков
ответ : 1.000000000000000000000000000000
б : 0.000000000000001000000000000000
с : 0.000000000000020000000000000000
д : 0.000000000000029999999999999998
сумма: 1.000000000000051100000000000000

ответ : 0.999999999999998787999878998887
б : 0.000000000000000999999997897899
с : 0.000000000000019999999999999458
д : 0.000000000000029999999999996589
sum1: 0.999999999999989000000000000000
Я пробовал double, long double и даже boost_dec_float, но результат, который я получаю, похож.

2 Ответов

Рейтинг:
1

CPallini

Взгляните на поплавок boost128-1.63.0[^].


Рейтинг:
0

Jochen Arndt

Большинство значений с плавающей запятой не могут быть представлены точно. В результате сохраненные значения немного отличаются от реальных. С двойной точностью можно представить 16 значащих десятичных цифр. Это означает, что отсчет ведется от первой ненулевой цифры, причем все цифры, находящиеся более чем в 16 позициях справа, не имеют значения (случайны при распечатке).

При выполнении операций now со значениями с плавающей запятой вводятся большие ошибки. Если, например, вы добавляете меньшее значение к большему, результирующая точность определяется большим значением:

  1.000 000 000 000 000 000
+ 0.000 000 000 000 001 xxx yyy
= 1.000 000 000 000 001 zzz

Цифры, отмеченные x и y в приведенном выше примере, будут потеряны, а z станет случайным (ноль в приведенном выше примере).

Выполнение большего количества операций с результатом увеличит количество ошибок. Это то, что вы видите.

Единственное решение, когда ошибки слишком велики, - это использование более точного числового формата. Пока long double если он может быть использован, вы должны проверить, поддерживается ли он на вашей платформе. Microsoft Visual Studio, например, не использует long double (этот long double тип на самом деле является double).

Вы также должны знать (и, вероятно, использовать) формат scientifc для чисел с плавающей запятой.

Вы можете использовать его, например, для замены pow() звонки:
//b=1*pow(10,-15);
//c=2*pow(10,-14);
//d=3*pow(10,-14);
b = 1e-15;
c = 2e-14;
d = 3e-14;

Он также может быть использован при печати значений с помощью printf функция. Такие выходные данные часто лучше читаются, чем множество конечных или ведущих нулей. Особенно в G формат полезен (он будет использовать научный формат только для малых и больших чисел):
printf("d: %.16G\n", d);
В приведенном выше примере точность ограничена 16 цифрами, так что нерелевантные цифры не будут напечатаны.

Поэтому вы должны попытаться использовать вышеуказанное форматирование в своей программе. Если результаты будут такими, как ожидалось (потому что нерелевантные цифры не печатаются, а вывод округляется), то все в порядке.


nv3

Блестящий ответ - но, видимо, для студента, которому явно лень читать учебник, как и многим на этом сайте. Мой 5.