Я рассчитал первые 20 элементов серии —
2 способами, 1 — вперед, 2 — назад. Для этого я сделал —
#include <iostream>
#include <math.h>
using namespace std;
float sumSeriesForward(int elementCount) {
float sum = 0;
for (int i = 0; i < elementCount; ++i) {
sum += (float) 1 / (pow(3, i));
}
return sum;
}
float sumSeriesBack(int elementCount) {
float sum = 0;
for (int i = (elementCount - 1); i >= 0; --i) {
sum += (float) 1 / (pow(3, i));
}
return sum;
}
int main() {
cout.precision(30);
cout << "sum 20 first elements - forward: " << sumSeriesForward(20) << endl;
cout << "sum 20 first elements - back: " << sumSeriesBack(20) << endl;
}
И я получил —
sum 20 first elements - forward: 1.5000001192092896
sum 20 first elements - back: 1.5
Может кто-нибудь объяснить, почему разница между этими двумя способами?
В общем случае числа с плавающей запятой не могут точно представлять значения. Когда вы оперируете значениями, ошибки распространяются. В вашем примере, когда вы вычисляете в обратном направлении, вы добавляете маленькие значения к все большим числам, и у вас есть хороший шанс, что сумма маленьких чисел до сих пор влияет на большее число. С другой стороны, когда вы вычисляете вперед, вы начинаете с больших чисел, и меньшие числа оказывают на них все меньшее влияние. То есть при суммировании вы всегда хотите подвести итог от наименьшего к наибольшему.
Просто подумайте о том, чтобы сохранить сумму в фиксированном количестве цифр. Например, оставьте 4 цифры и суммируйте эти числа сверху вниз и снизу вверх:
values top to bottom bottom to top
10.00 10.00 10.01
0.004 10.00 0.010
0.003 10.00 0.006
0.002 10.00 0.003
0.001 10.00 0.001
Числа с плавающей точкой работают точно так же, используя фиксированное число [двоичных] цифр.
Для повышения точности при суммировании чисел рассмотрим Алгоритм суммирования Кахана. Это значительно уменьшает ошибку по сравнению с очевидным подходом (включая суммирование чисел от наименьшего к наибольшему).