Click here to Skip to main content
15,887,683 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have the following mathematical problem:

If making reduction operations over large arrays creates rounding errors.
As example adding more than 100k samples using floats begin to generate errors of 1%

I solved partially using a reduction by using a tree adding by pairs (as it is said in this web for opencl apps: link)

My code at function "reduction()" works well to add the array elements.

unfortunately the function "reduction_media()" that add pairs and divide by 2 does not works. What is the error?

MODIFIED the code by add array filling again before usage , now it works.

What I have tried:

#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();
}
Posted
Updated 7-Sep-17 8:45am
v4
Comments
Mehdi Gholam 7-Sep-17 6:00am    
Try using decimal instead.
k5054 7-Sep-17 11:13am    
Decimal is quite slow, you may be able to get better results using double, or if your compiler supports it, long double.

On my linux box, compiling in 64 bit mode, I have the following:
float: 24 bit mantissa ~ 6 decimal digits
double: 53 bit mantissa ~ 15 decimal digits
long double: 64 bit mantissa ~ 18 decimal digits

If that's not appropriate for your use case, then you may wan to look at an arbitrary-precison math library, like GMP or MPFR.
k5054 7-Sep-17 11:42am    
On the other hand, in function reduction() you modify data[], but do not reset it, so you're getting the wrong answer when you call reduction_media().
Javier Luis Lopez 7-Sep-17 11:54am    
Thank you!. That is the answer, I updated the code.
Javier Luis Lopez 7-Sep-17 12:09pm    
It is true, using double you should use more large arrays to have problems. I had only errors when managing large matrix inverse. (My problem now was test opencl code at cpu. In opencl works faster using floats as needs less kernels)

long double = double when working in visual studio

Be aware using other libraries with more bytes mantissa can be too slow when working with large arrays.

1 solution

Quote:
Avoiding large arrays rounding error

There is no large array rounding error, your problem is about floating point limitations. Using a double datatype may solve your problem.

Your problem is related to mantissa range, the mantissa is limited.
This means that when you add a value large enough with a value small enough, on the result, the small value vanish.

Floating-point arithmetic - Wikipedia[^]
What Every Computer Scientist Should Know About Floating-Point Arithmetic[^]
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900