4 unsigned chars to float?

Started by
8 comments, last by alnite 8 years, 11 months ago

Since opengl ES 2.0 somehow doesnt support float textures i was forced to make them unsigned bytes so basically i have a float number and i need to convert it to 4 unsigned chars.

i cant use bit shifting and strings. (and i need to process data to texture at realtime.)

i am looking for a fast way to do this.

i dont need good precision my current function only accepts 5 digit numbers that are between 0..127*127 which is retarded anyway my function is retarded...

however it works for floats between 0..1 and for some other


struct TRGBA_FLOAT
{
Byte r;
Byte g;
Byte b;
Byte a;
TRGBA_FLOAT()
{
r=0;
g=0;
b=0;
a=0;
}

TRGBA_FLOAT(Byte n)
{
	r = n;
	g = n;
	b = n;
	a = n;
}

TRGBA_FLOAT(Byte ar, Byte ag, Byte ab, Byte aa)
{
r = ar;
g = ag;
b = ab;
a = aa;
}

};
//function returns 5 digit float number as a function of 4 bytes, accepts only positive numbers
TRGBA_FLOAT FloatToUnsignedChar(float num)
{
if (num > 16129.0)  return TRGBA_FLOAT(0);
if (num < -16129.0) return TRGBA_FLOAT(0);
   int MAX_RGBA_FLOAT_ITERATIONS = 5;
TRGBA_FLOAT res;
	//determine how big number is
//left number has higher priority and right side will loose precision
float p;
int num_len = 0;
if (num > 1.0)
{
p = num;
num_len = 0;
while ((p / 10.0) > 1.0)
{
	p = p / 10.0;
	num_len = num_len + 1;
}
p = num / 10.0;
num_len = num_len + 1;
}

int max_iterations = MAX_RGBA_FLOAT_ITERATIONS - num_len;

if (max_iterations == 0)
{

	if ( num >= 127.0 * 2.0 )
	{
	float x = num / 127.0;
	res.r = 127;
	res.g = Byte(int(x));//
	res.b = Byte(num - float(int(x) * 127.0));//Byte(int(mynumber - float(res.g)));
	res.a = 0;
	}  else
		if (num <= 127.0)
		{
res.r = Byte(num);
res.g = 1;
res.b = 0;
		}
		else
		{
			res.r = 127;
			res.g = 1;
			res.b = Byte(num) - 127;
		}


		res.a = 0;

return res;

}

//lets try to calculate number of decimals
int i = int(num);
p = num - float(i);



int decnum = 0;
//0.045
//0.45
//4.5
//0.5
//5 - 5
int return_number=0;

bool stop = false;
int iterations = 0;
while (!stop)
{
if (decnum == max_iterations) break;
	iterations = iterations + 1;
	if (iterations > max_iterations) break;
	if (p < 1.0)
	{
	p = p * 10.0;
	decnum = decnum + 1;
		if (p == 0.0) break;
		continue;
	}

if (p > 1.0)
{
p = p - float(int(p));
decnum = decnum + 1;
if (p == 0.0) break;
}



}
float sth = 0.0;
 if (decnum == 0) sth = int(pow(10, num_len));
decnum = decnum - 1;
//77.891
p = num - float(int(num));
for (i=0; i < decnum; i++) p = p * 10;
//891

float mynumber = float(int(num) * int(pow(10, num_len)) + int(p)) - sth ;
//77891


	if ( mynumber >= 127.0 * 2.0 )
	{
	float x = mynumber / 127.0;
	res.r = 127;
	res.g = Byte(int(x));//
	res.b = Byte(mynumber - float(int(x) * 127.0));//Byte(int(mynumber - float(res.g)));
	}     else
		if (mynumber <= 127.0)
		{
res.r = Byte(mynumber);
res.g = 1;
res.b = 0;
		}
		else
		{
			res.r = 127;
			res.g = 1;
			res.b = Byte(mynumber) - 127;
		}

res.a = Byte(decnum);


return res;



	}

 float RGBA_FLOAT_TO_FLOAT(TRGBA_FLOAT num)
 {
 float kp = float(int(num.r*num.g+num.b));
 for (int i=0; i < num.a; i++)
kp = kp / 10.0;
return kp;
 }

Advertisement
I'm... not sure you're doing what you think you're doing? The wording of your question seems to imply you've got some texture that uses float values for color and you want to use byte values instead, in which case I'm pretty sure your code is wrong anyway, and the solution I'm proposing won't work either.

If, however, your only goal is to give a float values to someone via your little TRGBA_FLOAT struct, the following will work:

reinterpret_cast will do what you need - assuming all you do is convert it back to a float before you use it, don't try to manipulate the four bytes directly, and don't actually care what the byte values are.

This code will perfectly convert, no loss of precision, to and from any float.

To go from float to four bytes:


TRGBA_FLOAT FloatToUnsignedChar(float num)
{
  // NOTE: This code assumes 32-bit floats.
  Byte* floatAsBytes = reinterpret_cast<Byte*>(&floatValue); // tell the compiler to pretend the float is an array of four bytes
  return TRGBA_FLOAT(floatAsBytes[0], floatAsBytes[1], floatAsBytes[2], floatAsBytes[3]);
}
To go from those bytes back to a float:


float RGBA_FLOAT_TO_FLOAT(TRGBA_FLOAT num)
{
   // NOTE: We could do this in one line, but that's potentially getting too clever as it would break with
   // any padding in the struct and if you re-arranged your struct values
   Byte bytesForFloat[4] = {num.r, num.g, num.b, num.a};
   return *(reinterpret_cast<float*>(bytesForFloat); // tell the compiler to pretend the array of four bytes is a float
}

how can i write that second function to be used in glsl? it happens that after uploading this to texture i will have 4 float components in range of 0..1

now i will somehow have to convert it back to float

how can i write that second function to be used in glsl? it happens that after uploading this to texture i will have 4 float components in range of 0..1
now i will somehow have to convert it back to float


The problem is the IEEE single precision floating point format doesn't have a simple binary representation. ES2 doesn't support bitwise manipulations which doesn't help either.

Anyway - this is off the top of my head so you'll have to check it for errors:

float tex2float(vec4 tex)
{
   tex = tex * 255;  // Convert 0..1 range to 0..255.
   int exp = (tex.r > 127 ? tex.r - 128 : tex.r) * 2 + tex.g > 127 ? 1 : 0; // 7 LSB of r and 1 MSB of g.

   if (exp == 0)     // Exponent == 0 for very small numbers (< 1 * 2^-126) so treat these as zero.
      return 0;

   if (exp == 255)   // Exponent == 255 means + or - infinity. You can probably ignore this.
      return (tex.r > 127 ? -1 : 1) / 0;

   float sig = 128 + tex.g > 127 ? tex.g - 128 : tex.g; // Significand is top bit set plus the 7 LSB of g.
   sig = sig * 65536 + tex.b * 256 + tex.a;             // Combine it with b and a.
   if (tex.r > 127)                                     // If the sign bit is set negate the significand.
      sig = -sig;

   return sig * exp2(exp-(127-23)); // Multiply by the exponent (with the exponent bias and 23 significand bits applied).
}

#define _2pow23  8388608
#define log10_2pow23_1 6.923689f
#include <math.h>
#include <allocators>
float rgba_to_float(float r, float g, float b, float a)
{
 float inp = (float)(256.0f * (b + (g + r * 256.0f) * 256.0f) - (float)_2pow23)*pow(10.0f, a * 256.0f - 128.0f);
 return inp;
}
float *float_to_rgba(float inp)
{
 float * rgba = (float*)malloc(sizeof(float) * 4);
 if (inp == 0)
 {
  rgba[0] = 0;
  rgba[1] = 0;
  rgba[2] = 0;
  rgba[3] = 0;
  return rgba;
 }
 rgba[3] = float(int(log10(abs(inp)) - log10_2pow23_1)) / 256.0f;
 long int _24bit_int = int(inp*pow(10, -256.0f*rgba[3])) + _2pow23; //make it positive
 rgba[3] += 0.5;
 rgba[2] = float(_24bit_int % 256) / 256.0f;
 _24bit_int = int(float(_24bit_int) / 256.0f);
 rgba[1] = float(_24bit_int % 256) / 256.0f;
 _24bit_int = int(float(_24bit_int) / 256.0f);
 rgba[0] = float(_24bit_int % 256) / 256.0f;
 return  rgba;
}

this code is just the representation of the algorithm.

first step is to find a way to represent a 32 bit float, for this we can use a 24bit integer (signed) and an 8bit integer (signed). so our float will be represented as value = Number* 10Exponent where Exponent will be an 8bit integer and Number will be a 24 bit integer.

the second step is to find the Exponent. as we now a 24bit signed integer can hold values of [-8388608, 8388608) range ( [-223,223) ). so our 24bit integer should not exceed this range.

but we also need to use the greatest number we can fit into a 24bit integer so that we can have the best precision we can get of a 24bit integer.

so to achieve this we can multiply our float to 10 in a loop and stop just before our float exceeds the range of our integer.(now many may think this will cost a lot to process, so do I, so math gives us a solution)

so what we actually did is like finding the number of our floats digits and subtract it from number of the digits that we can put in our 24bit integer (lets consider this value as P, so P=number of digits we can put in our integer - number of digits our of our float) . and then we multiplied our float with 10P. the only problem was it was too costly to calculate P.

no we may look back at the way we calculated P: P=number of digits we can put in our integer - number of digits our of our float

lets say the maximum number of digits we can put in our 24bit integer is n so we can say:

10n+1>223-1>=10n

=> log10(10n+1)>log10(223-1)>=log10(10n)

=> n+1>log10(223-1)>=n

=> ([ ] is used as a representation of the floor function) [log10(223-1)]=n

so n is a constant number but to get better use of log10 and use the maximum number we can fit in our integer we will use n as log10(223-1) (in the code defined as log10_2pow23_1) and not [log10(223-1)].

then we have to calculate the number of our digits on our float the same way, so :

(number of float's digits) fn = log 10(value) (in code as log10(abs(inp)) )

so now we can calculate our P using n and fn : P=[n-fn] ([ ] is used as a representation of the floor function)

now we can say: value = value*10P*10-P

as we defined P, we know that value*10P is the value we can set to our 24bit integer and also use the best of our 24bit integer, so in our representation of float (value = Number* 10Exponent):

Number = [value*10P] ([ ] is used as a representation of the floor function) and Exponent = - P as we have value = Number*10-P

so now we have represented our float as two integers next step (third step) is to convert these integers to 4 unsigned bytes.

as we defined Exponent is just an 8bit (one byte) integer the only problem is that it can be negative.

to get rid of the negative values we sum exponents with 128.

the quiet same will be done over the Number (our 24 bit integer) but it will be summed by 223 we'll call the new values as Number' and Exponent'.

the Exponent' is now ready to be sent as a byte to the texture.

forth step: chopping up the 24bit integer to three bytes.

we know that we can represent our 24bit integer as a unique three digit number in base 256.

we will use these digits to represent Number' =(RGB)256

we know that 0 <=R , G , B < 256 so R and G and B can be used as our bytes in texture.

to retrieve our R, G and B we'll do a simple base conversion: R=[Number'/2562]%256; B=[Number'/256]%256; G=Number'%256;

so now we have converted our float to 4 bytes

next step is reconstructing the float using these bytes:

our float is (R,G,B,A), first we reconvert (RGB)256 to base 10 to reconstruct Number': Number'=R*2562+G*256+B (this is optimized in program to (R*256+G)*256+B)

we know Number'=Number+223 :

=> Number=Number' - 223

A is our Exponent' so to retrieve Exponent we use :

Exponent'= Exponent+128

Exponent= Exponent'-128

so now we have retrieved our Exponent and Number so our float can be reconstructed : value = Number* 10Exponent

EDIT: I had mistake calculating the range of 24bit integer

and a simple test code for the functions :


#include <iostream>
using namespace std;

int main()
{
	float *testfloat;
	float value;
	for (float a = -10.3865f; a < 355.2426; a += 1.6456)
	{
		value = a;
		testfloat = float_to_rgba(value);
		float resault = rgba_to_float(testfloat[0], testfloat[1], testfloat[2], testfloat[3]);
		cout << "Float value:" << value << "    Float value after conversion:" << resault << "   Erorr:" << float(value - resault) << '\n';
		free(testfloat);
	}
	cin >> value;
}

the error of conversion is quiet proportional to the value of the float so greater value means greater error.

EDIT2: sorry there was a little bug in the code causing the greater errors I edited the functions there is now almost no error in conversion

how can i write that second function to be used in glsl? it happens that after uploading this to texture i will have 4 float components in range of 0..1
now i will somehow have to convert it back to float


If you need to get at the float value in glsl, then my solution won't work.

You may want to elaborate on what exactly you're trying to accomplish. There are several ways to represent numbers with fractional values, and your use case may help indicate which is better. Or even if you need float values in the first place.

As this is a graphics/OpenGL question, I'll step out and let the experts take over smile.png


base 10 2

fixd

o3o

well i am using my code i showed you a litle bit modified coz it had bugs, since i can define a 'vector' that represents value from minimum to maximum (0..1) my function works fine

well i am using my code i showed you a litle bit modified coz it had bugs, since i can define a 'vector' that represents value from minimum to maximum (0..1) my function works fine


If you're still interested in using floats as texture data directly, i.e. lying to GL that your block of memory containing floats is RGBA texture data and reconstructing the floats in the fragment shader, I've corrected the function I posted above so it will convert a float read as texture data into the original float. I've restricted it to use only language constructs that are available in the version of GLSL associated with ES 2.0 and wrapped it in a little program so you can play around with it.


#include <stdio.h>
#include <stdint.h>
#include <glm/glm.hpp>

static const float TwoExp23 = exp2(23.0f);

float tex2float(glm::vec4 tex)
{
   tex *= 255.0f;    // Scale texture values so we can more easily deal with bits.
   
   int exp = (tex.a > 127.0f ? tex.a - 128.0f : tex.a) * 2.0f + (tex.b > 127.0f ? 1.0f : 0.0f); // 7 LSB of a and 1 MSB of b.

   if (exp == 0.0f)     // Exponent is zero for zero and for very small numbers (< 1 * 2^-126) so treat all these as zero.
      return 0.0f;

   if (exp == 255.0f)   // Exponent == 255 means + or - infinity and NaN. We deal with +/- infinity here but you can probably ignore it.
      return (tex.a > 127.0f ? -1.0f : 1.0f) / 0.0f;

   float sig = tex.b > 127.0f ? tex.b - 128.0f : tex.b;  // Significand is the 7 LSB of b...
   sig = sig * 65536.0f + tex.g * 256.0f + tex.r;        // ...followed by g and r.
   sig = 1.0f + sig / TwoExp23;                          // Scale to < 0 and add the implied 1 at the beginning.
   if (tex.a > 127.0f)                                   // If the sign bit is set negate the significand.
      sig = -sig;
   
   return sig * exp2(exp-127.0f);                        // Multiply by the exponent (with the exponent bias applied).
}

int main(int argc, char **argv)
{
   float f1 = -1234.5678f;

   uint8_t *bytes = reinterpret_cast<uint8_t*>(&f1);

   glm::vec4 tex = glm::vec4(bytes[0], bytes[1], bytes[2], bytes[3]);
   
   tex /= 255.0f;       // Texture values are normalized to 0..1 so scale down the bytes to fit.

   float f2 = tex2float(tex);

   printf("f1 = %f\n", f1);
   printf("f2 = %f\n", f2);

   return 0;
}
Having said that, you probably ought to check the precision of texture data (using glGetIntegerv with GL_x_BITS where x is RED, GREEN, BLUE or ALPHA) to make sure they are all 8. If they aren't, you won't be able to use floats directly and will have to do some sort of conversion after all.

Additionally, floats in fragment shaders might not have the full precision that they do on desktop hardware - they are allowed to have as few as 10 binary digits (which translates to just over 3 decimal digits). So don't be surprised if the reconstructed floats don't match the input ones exactly.

You can find the OpenGL ES documentation describing all this at the Khronos site.

I don't quite understand the original question, but if you just need the float in 4-unsigned-chars format, why not use union?


#include <stdio.h>

int main() {
  union {
    float v;
    unsigned char c[4];
  };

  v = -9000.50f;
  printf("Float value: %f\n", v);
  printf("Byte values: ");
  for (int i = 0; i < 4; ++i) {
    printf("%02x ", c[i]);
  }
  printf("\n");
}

Output:


Float value: -9000.500000
Byte values: 00 a2 0c c6 

Although, big/little endian will bite you hard in the ass. Make sure you know the endianess of your system, and anything else it sends this data to.

This topic is closed to new replies.

Advertisement