Sign in to follow this  
graveyard filla

could someone please explain this to me (16 bit float)

Recommended Posts

hi, im working on an small scale online RPG. anyway, i was thinking about how i will store positions, and someone showed me this code for a 16 bit float. ive emailed the guy who wrote the code but he hasnt responded, so im posting here. heres the source:

/*
COPYRIGHT (C) 2003 - DANUSHKA ABEYSURIYA
contact: silvermace007@hotmail.com

this code is provided 'as is' the author (Danushka Abeysuriya) takes
no responsibilty for any damage caused directly or indirectly by use of this
software or any portion of this source code.

this source code is freely availible and is provided as 'open-source'
under the terms and conditions of the GNU GPL license
*/

/*
this is a simple 1dp observed error table

 VALUE |  ERROR
 ------|-----------
       |
 0.9   |  0.003175   --+
 0.8   |  0.006349     |
 0.7   |  0.001587     +-  there is a distinct pattern. its basicaly constant error.
 0.6   |  0.004762     | 
 0.5   |  0.000000   --+
       |
 0.4   |  0.003175 - 0.003175
 0.3   |  0.006349 - 0.003174
 0.2   |  0.001587 - 0.004762
 0.1   |  0.004762 - 0.003175
 0.0   |  0.000000 - 0.004762
 
*/

#ifndef __CFLOAT_TMPLATE__
#define __CFLOAT_TMPLATE__

//add this for precison compensation
const float PRECISON_ERROR_EVN = 0.003175F;
const float PRECISON_ERROR_ODD = 0.004762F;
//003175F

//16bit float class
template<class storage_type>
class cfloat
{
public:
	storage_type _compressed;
	static int _scalar;
	static float *_errortable, *_errortable_2dp;
	
	//average error works surprisingly well!
	static float PRECISON_ERROR_AVG, PRECISON_ERROR_2DP;

	//use this to define the float16<type>
	static void SetCFloatScalar() {
		//scalar = (((1 << sizeof(storage_type) << 3)/2)-2);
		_scalar = (((sizeof(storage_type) << 7)/2)-2);
		//cout << _scalar << endl;
	}

	static void SetErrorLookupTable()
	{
		if( !_errortable )
			_errortable = new float[10];
		if( !_errortable_2dp )
			_errortable_2dp = new float[10];
			
		storage_type _cval;
		float avg = 0.0F;
		int i=0;
		
		cout << "Error Lookup Table" << endl << "------------------------" << endl;
		//create an error lookup table
		for(i=0; i<10; i++)
		{
			float test = (float)i/10.0f;
			
			_cval = (storage_type)(test * _scalar);
			_errortable[i] = fabs(test - ((float) _cval / (float) _scalar));
			
			cout << test << " | " << _errortable[i] << endl;
			
			avg += _errortable[i];
		}
		
		PRECISON_ERROR_AVG = avg / 10.f;
		cout << "\n2dp Error Lookup Table" << endl << "------------------------" << endl;
		
		avg = 0.0f;
		for(i=0; i<10; i++)
		{
			float test = (float)i/100.0f;
			
			_cval = (storage_type)(test * _scalar);
			_errortable_2dp[i] = fabs(test - ((float) _cval / (float) _scalar));
			
			cout << test << " | " << _errortable_2dp[i] << endl;
			
			avg += _errortable_2dp[i];			
		}
		
		PRECISON_ERROR_2DP = avg / 10.f;
		
		cout << "Average 2dp Error: " << PRECISON_ERROR_2DP << endl << endl;
	}
	
	static void Release() {
		if( _errortable )
			delete [] _errortable;
			
		if( _errortable_2dp )
			delete [] _errortable_2dp;
	}
	
	cfloat(float val) {
		_compressed = 0;
		quantize(val);
	}
	
	cfloat() : _compressed(0) {}
	
	//////////// NOT TESTED /////////////
	float operator=(float& rhs) {		
		quantize(val);
		return convert();
	}
	/////////////////////////////////////

	//odd/even error method
	float _float_oe() {
		return convert_oddeven();
	}
	
	//error average method
	float _float_ae() {
		return convert_average();
	}
	
	float _pure() {
		return (float) _compressed / (float) _scalar;
	}
	
	//std::cout << (((1 << sizeof(type) << 3)/2)-2) << std::endl;
private:
	//compresses a float
	inline void quantize(float val) 
		{ _compressed = (storage_type)(val * _scalar); }


	//converts a quantized float back to a regular
	//upper interger limit is 259
	float convert_oddeven() 
	{
		float _flt = (float) _compressed / (float) _scalar;
		
		float _mantissa = _flt - floor( _flt );
		float _error = 0.f;
		
		float modulus = 0.89f;
		
		int _level = (int)(floor(10 * ( _mantissa + 0.01F )));

		switch(_level) {
			case 1: case 6: _error = _errortable[1]/*0.004762F*/; break;
			case 2: case 7: _error = _errortable[2]/*0.001587F*/; break;
			case 3: case 8: _error = _errortable[3]/*0.006349F*/; break;
			case 4: case 9: _error = _errortable[4]/*0.003175F*/; break;
		}
		
		//_error += 0.00206350F;
		_error *= modulus;
		
		return (_flt + _error);
	}	
	
	inline float convert_average()
	{
		return _pure() + PRECISON_ERROR_AVG;
	}
};

#endif //__CFLOAT_TMPLATE__

and heres an example program using it..

/*
COPYRIGHT (C) 2003 - DANUSHKA ABEYSURIYA
contact: silvermace007@hotmail.com

this code is provided 'as is' the author (Danushka Abeysuriya) takes
no responsibilty for any damage caused directly or indirectly by use of this
software or any portion of this source code.

this source code is freely availible and is provided as 'open-source'
under the terms and conditions of the GNU GPL license

compile command: "g++ -W -Wall _float.cpp -o flt -O6"
on G++ 3.xx

*/

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cmath>
#include <ctime>

#include "cfloat"

using namespace std;

//this is the storage type
typedef unsigned short storage;

//init static values for the float class
int 	cfloat<storage>::_scalar = 0;
float   cfloat<storage>::PRECISON_ERROR_AVG = 0.0F;
float   cfloat<storage>::PRECISON_ERROR_2DP = 0.0F;
float  *cfloat<storage>::_errortable = NULL;
float  *cfloat<storage>::_errortable_2dp = NULL;

const int _testAmt = 3000;

//tester class
const char dumpfloat[] = "test_float.bin";
const char dumpcompressed[] = "test_compressed.bin";

class CFloatTestbed
{
private:
	bool _testFileWriten;
	bool _output;
	float average_error;
	float average_pureerror;

public:
	CFloatTestbed(bool output) : _testFileWriten(false), _output(output) {
		
		//init the storage class
		cfloat<storage>::SetCFloatScalar();
		cfloat<storage>::SetErrorLookupTable();
		
		average_error = 0.0F;
		average_pureerror = 0.0F;
	}
	
	~CFloatTestbed() {}
	
	void Test()
	{
		//create a test file
		WriteTestFile_NonCompressed();	
		WriteTestFile_UseCompressed();	
		
		//average_error /= (float) _testAmt;
		
		float dv = (float)_testAmt;
		
		cout << "\naverage error: " << average_error/dv << endl;
        cout << "average pure error: " << average_pureerror/dv << endl;
	}
	

private:
	void WriteTestFile_NonCompressed()
	{
		ofstream file(dumpfloat, ios::binary);
		
		srand(time(NULL));
		float val = 0.f;
			
		for(int i=0; i<_testAmt; i++) {
			if( sizeof(storage) >= 2 )
				val = (float((float)(rand() % 211)/3.2355F));
			else
			{
				val = (float((float)(rand() % 1000)/1000.0F));
			}
			
			file.write(&val, sizeof(float));
		}
		
		file.close();
		_testFileWriten = true;
	}

	void WriteTestFile_UseCompressed()
	{
		if( !_testFileWriten)
			return;
		
		ifstream infile (dumpfloat, ios::binary);
		ofstream outfile(dumpcompressed, ios::binary);
		
		for(int i=0; i<_testAmt; i++) {
			float val = 0.0f;
			infile.read(&val, sizeof(float));
					
			cfloat<storage> _cfloat(val);
			
			float val2 = _cfloat._float_ae();
			
			storage compressed = _cfloat._compressed;
			outfile.write(&compressed, sizeof(storage));
			
			if( _output )
				cout << val << " --> " << val2 << endl << flush;
			
			average_error = (float)(average_error + fabs(val - val2));
			average_pureerror = (float)(average_pureerror + fabs(val - _cfloat._pure()));
		}
	}

};

int main(int argc, char **argv)
{

	CFloatTestbed _tester(true);
	
	_tester.Test();
		
	//cleanup
	cfloat<storage>::Release();
	
	return 0;
}

so, can anyone explain to me how this works exactly? it seems to be fairly accurate, and would allow me to store my positions as floats still, and then cast to a 16 bit float when sending over the wire, which is a very nice alternative to me then creating my own fixed point type. however, i dont want to just use this code without understanding it, and i dont get how any of it is working... thanks for any help

Share this post


Link to post
Share on other sites
Doc    586
Where to start? It'd be helpful if you told us what parts, if any, you do understand. For starters, do you understand the template notation?

Share this post


Link to post
Share on other sites
hey Doc,

sorry, i should have been more clear.. yes, i understand the template part. (im guessing he made it a templated class so you could make it an 8 bit or 32 bit or X bit float if you wanted..) what i dont understand is how it all works really, and how im suposed to use it. i know in the example it seems like it takes a number from a file, then reads it into a float and then into an instance of the class.. however, im not sure how it works exactly, like, why is the float member of the class static? i notice it does something like divide the 2 byte value by "Scaler" to get the "real" float, at least i think.... but im just majorily confused by the whole thing.

thanks for any help.

Share this post


Link to post
Share on other sites
silvermace    634
hi dude, very very sorry about not responding, i've been totaly swamped with work. the template param is the storage type, so if you want 16 bits, you would use unsigned short (2-bytes)

so you can basically ignore the template arg by using a typedef!
typedef cfloat<unsigned short> float16_t;
now, if you don't want to play around with the error correction (its alright, but i cant guarantee anythihg ;) you dont need know about these functions:
SetErrorLookupTable, Release, _float_ae, _float_oe

btw, i think the "operator=" is wrong, replace "convert()" with "_pure()"

here's some basic usage:

#include <cfloat>
typedef cfloat<unsigned short> float16_t;

// must call this at least once in the templates life.
float16_T::SetCFloatScalar();

float16_t myFloat(50.0f);
float temp = (float)(myFloat) * 3;
printf( "%f\n", temp );


EDIT: Hm.. i might write you a special cleaned up version.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this