Избегание ошибки округления больших массивов
У меня есть следующая математическая задача:
При выполнении операций сокращения над большими массивами возникают ошибки округления.
Как пример, прибавив в цене более 100 тыс. образцов, используя поплавки начинают генерировать ошибки 1%
Я решил частично, используя сокращение с помощью добавления дерева по парам (как сказано в этом вебе для приложений opencl: ссылка)
Мой код в функции "reduction ()" хорошо работает для добавления элементов массива.
к сожалению, функция " reduction_media ()", которая добавляет пары и делит на 2, не работает. В чем же ошибка?
Модифицировал код, снова добавив заполнение массива перед использованием, теперь он работает.
Что я уже пробовал:
#include <iostream> #include <time.h> //clock() #pragma warning(disable:4996) //disable deprecateds using namespace std; typedef unsigned char uchar; time_t start,stop; //Timing function. Add this command to the beginning of the function to be measured: start=clock(); void timer(char *title,int size=0) { stop=clock(); cout<<title<< " time ="<<(double) (stop-start)/(double) CLOCKS_PER_SEC<< " secs"; if (size) cout << " = " << 1e-6*size/( (double)(stop-start)/(double)CLOCKS_PER_SEC ) << " Mops/seg" <<endl; else cout<<endl; start=clock();//it must be done better in the beginning of the function to be measured } #define SIZE 100000000 //100Meg //tree reduction: float reduction(float *data,int max) { while(max>1) { data[max]=0.0;max=(max+1)/2; for (int i=0;i<max;i++) data[i]+=data[i+max]; //operation to be performed } return data[0]; } //calculates media NOW WORKING! float reduction_media(float *data,int max) { while(max>1) { data[max]=data[max/2];max=(max+1)/2; for (int i=0;i<max;i++) data[i]=(data[i]+data[i+max])/2.0f; //operation to be performed } return data[0]; } void main() { cout<<"WARNING: run this program in RELEASE mode. Timing is non correct in DEBUG mode"<<endl; float *data=new float[SIZE+1]; //Fills the array in a very fast way: std::fill(data, data+SIZE, 3.14f); timer("\n fill(data, data+SIZE, 3.14f) ",SIZE); //Calculates media in standard way but FAILS due rounding errors: start=clock(); float sum=0.0f; for (int i=0;i<SIZE;i++) sum+=data[i]; sum=sum/(float) SIZE; timer("\n Standard for() reduction ",SIZE); cout << "This should be 3.14="<<sum<<endl; //calculates media by using the tree reduction function, is OK and runs 20% faster in release mode: start=clock(); sum=reduction(data,SIZE)/(float) SIZE; timer("\n Using reduction() function ",SIZE); cout << "This should be 3.14="<<sum<<endl; //calculates media by using reduction_media: std::fill(data, data+SIZE, 3.14f); start=clock(); sum=reduction_media(data,SIZE); timer("\n Using reduction() function ",SIZE); cout << "This should be 3.14="<<sum<<endl; delete data; cout<<"===END==="<<endl;getchar(); }
Mehdi Gholam
Попробуйте вместо этого использовать десятичную дробь.
k5054
Decimal работает довольно медленно, вы можете получить лучшие результаты, используя double, или, если ваш компилятор поддерживает его, long double.
На мою Linux, компиляция в 64 битном режиме, у меня есть следующие:
поплавок: 24-битная мантисса ~ 6 десятичных цифр
двойной: 53-битная мантисса ~ 15 десятичных цифр
длинный двойной: 64-битная мантисса ~ 18 десятичных цифр
Если это не подходит для вашего варианта использования, то вы можете посмотреть на математическую библиотеку произвольной точности, такую как GMP или MPFR.
k5054
С другой стороны, в функции reduction() вы изменяете данные [], но не сбрасываете их, поэтому вы получаете неправильный ответ при вызове reduction_media().
Javier Luis Lopez
Спасибо!. Вот и ответ, я обновил код.
Javier Luis Lopez
Это правда, используя double, вы должны использовать более большие массивы, чтобы иметь проблемы. У меня были только ошибки при управлении большой обратной матрицей. (Теперь моя проблема заключалась в тестировании кода opencl на процессоре. В opencl работает быстрее, используя поплавки, так как требуется меньше ядер)
long double = double при работе в visual studio
Имейте в виду, что использование других библиотек с большим количеством байтов мантиссы может быть слишком медленным при работе с большими массивами.