# Re: Convert from unsigned char array to float!

Discussion in 'C Programming' started by stanley, Jun 7, 2007.

1. ### stanleyGuest

i've digged this out because i have a similar issue

i have a rgb-frame in an unsigned char array which holds the channel
values in hex
the frame is 10bit per channel, meaning the there are two bytes used for
each channel - the byteorder is therefore not like in a normal rgb-array
(r g b r g b)
it is: rr gg bb

my idea was to read out the two bytes of each channel an convert them
with the posted code, combine(add) them afterwards, while multiplying
the upper part with 256.

any idea if this will work?

Code (Text):

convert_frame_float(unsigned char *frame){

const unsigned char* tmp_frame = frame;
double* floatframe; int j = 0;

for (i=0; i<size; i+=2){

unsigned long upper = (unsigned long)tmpframe[i];
unsigned long lower = (unsigned long)tmpframe[i+1];
floatframe[j] =
256*decode_ieee_single(upper,1)+decode_ieee_single(lower,1); j++;
}
return floatframe;
}
--

I would suggest that you have made in mistake in your
calculations. Here is some code that does it for you:

\$ type ieee2float.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <assert.h>

double decode_ieee_single(const void *v, int natural_order)
{
const unsigned char *data = v;
int s, e;
unsigned long src;
long f;
double value;

if (natural_order) {
src = ((unsigned long)data[0] << 24) |
((unsigned long)data[1] << 16) |
((unsigned long)data[2] << 8) |
((unsigned long)data[3]);
}
else {
src = ((unsigned long)data[3] << 24) |
((unsigned long)data[2] << 16) |
((unsigned long)data[1] << 8) |
((unsigned long)data[0]);
}

s = (src & 0x80000000UL) >> 31;
e = (src & 0x7F800000UL) >> 23;
f = (src & 0x007FFFFFUL);

if (e == 255 && f != 0) {
/* NaN (Not a Number) */
value = DBL_MAX;
}
else if (e == 255 && f == 0 && s == 1) {
/* Negative infinity */
value = -DBL_MAX;
}
else if (e == 255 && f == 0 && s == 0) {
/* Positive infinity */
value = DBL_MAX;
}
else if (e > 0 && e < 255) {
/* Normal number */
f += 0x00800000UL;
if (s) f = -f;
value = ldexp(f, e - 150);
}
else if (e == 0 && f != 0) {
/* Denormal number */
if (s) f = -f;
value = ldexp(f, -149);
}
else if (e == 0 && f == 0 && s == 1) {
/* Negative zero */
value = 0;
}
else if (e == 0 && f == 0 && s == 0) {
/* Positive zero */
value = 0;
}
else {
/* Never happens */
printf("s = %d, e = %d, f = %lu\n", s, e, f);
assert(!"Woops, unhandled case in decode_ieee_single()");
}

return value;
}

int main(void)
{
unsigned char f[4] = {0xde, 0xc2, 0x44, 0x23};

printf("0x%02X%02X%02X%02X as an IEEE float is %f\n",
f[0], f[1], f[2], f[3], decode_ieee_single(f, 0));

printf("0x%02X%02X%02X%02X as an IEEE float is %f\n",
f[3], f[2], f[1], f[0], decode_ieee_single(f, 1));

return 0;
}

\$ cc ieee2float
\$ run ieee2float
0xDEC24423 as an IEEE float is 0.000000
0x2344C2DE as an IEEE float is -6999176012340658200.000000

This code has not been exhastively tested. The above output
was generated on system not using IEEE floating point, but its
output agrees with your IEEE system.

Phil T

stanley, Jun 7, 2007

2. ### Ben BacarisseGuest

stanley <> writes:

> i've digged this out because i have a similar issue
>
> i have a rgb-frame in an unsigned char array which holds the channel
> values in hex
> the frame is 10bit per channel, meaning the there are two bytes used
> for each channel - the byteorder is therefore not like in a normal
> rgb-array (r g b r g b)
> it is: rr gg bb

This sounds a little odd. The usual meaning of "values in hex" is
that the values is stored using some character set to represent hex
digits. Thus two bytes == two characters == 2 hex digits == 8 bits so
string 10 bits is not possible.

> my idea was to read out the two bytes of each channel an convert them
> with the posted code, combine(add) them afterwards, while multiplying
> the upper part with 256.

Ah, now that suggests that the bytes just hold unsigned integers.
Much more reasonable...

> any idea if this will work?

I would think so.

> convert_frame_float(unsigned char *frame){

^ no return type.

>
> const unsigned char* tmp_frame = frame;

You don't gain much by adding this. Make the parameter point to const
if you want to show that this function does not modify the frame.

> double* floatframe; int j = 0;

You have not allocated storage for floatframe to point to. If you so
anything at all with this uninitialised pointer, you will have
problems.

It looks like you need:

double *floatframe = malloc(size/2 * sizeof *floatframe);

> for (i=0; i<size; i+=2){
>
> unsigned long upper = (unsigned long)tmpframe;
> unsigned long lower = (unsigned long)tmpframe[i+1];
> floatframe[j] =
> 256*decode_ieee_single(upper,1)+decode_ieee_single(lower,1); j++;

I see no need to use any decoding function. "256 * upper + lower" will
do just fine.
> }
> return floatframe;

This suggests you wanted the function return a "double *".
> }

--
Ben.

Ben Bacarisse, Jun 7, 2007

3. ### stanleyGuest

Ben Bacarisse wrote:
> stanley <> writes:
>
>> i've digged this out because i have a similar issue
>>
>> i have a rgb-frame in an unsigned char array which holds the channel
>> values in hex
>> the frame is 10bit per channel, meaning the there are two bytes used
>> for each channel - the byteorder is therefore not like in a normal
>> rgb-array (r g b r g b)
>> it is: rr gg bb

>
> This sounds a little odd. The usual meaning of "values in hex" is
> that the values is stored using some character set to represent hex
> digits. Thus two bytes == two characters == 2 hex digits == 8 bits so
> string 10 bits is not possible.

very true - two bytes are filled with the characters representing the
hex digits

thus the decoding function will be very neccessary (?)
also i have allocated memory for *floatframe and i simply forgot
to post it - same with the return type - sorry for that

here is the complete code snippet (decode_ieee_single is in the post above):

Code (Text):

double* convert_frame_to_float(unsigned char* frame, unsigned long int
size){

int j = 0;
double* floatframe;

if((floatframe = (double *) malloc(size)) == NULL){
exit(1);
}
memset(floatframe, 0.0, (size));
//leaving out *tmp_frame
for(int i=0; i<size; i+=2){
unsigned long upper = (unsigned long)frame[i];
unsigned long lower = (unsigned long)frame[i+1];

floatframe[j] = 256.0 * decode_ieee_single(upper,1) +
decode_ieee_single(lower,1);
j++;
}
return floatframe;
}

stanley

>
>> my idea was to read out the two bytes of each channel an convert them
>> with the posted code, combine(add) them afterwards, while multiplying
>> the upper part with 256.

>
> Ah, now that suggests that the bytes just hold unsigned integers.
> Much more reasonable...
>
>> any idea if this will work?

>
> I would think so.
>
>> convert_frame_float(unsigned char *frame){

> ^ no return type.
>
>> const unsigned char* tmp_frame = frame;

>
> You don't gain much by adding this. Make the parameter point to const
> if you want to show that this function does not modify the frame.
>
>> double* floatframe; int j = 0;

>
> You have not allocated storage for floatframe to point to. If you so
> anything at all with this uninitialised pointer, you will have
> problems.
>
> It looks like you need:
>
> double *floatframe = malloc(size/2 * sizeof *floatframe);
>
>> for (i=0; i<size; i+=2){
>>
>> unsigned long upper = (unsigned long)tmpframe;
>> unsigned long lower = (unsigned long)tmpframe[i+1];
>> floatframe[j] =
>> 256*decode_ieee_single(upper,1)+decode_ieee_single(lower,1); j++;

>
> I see no need to use any decoding function. "256 * upper + lower" will
> do just fine.
>> }
>> return floatframe;

>
> This suggests you wanted the function return a "double *".
>> }

>

stanley, Jun 7, 2007
4. ### Ben BacarisseGuest

stanley <> writes:

> Ben Bacarisse wrote:
>> stanley <> writes:
>>
>>> i've digged this out because i have a similar issue
>>>
>>> i have a rgb-frame in an unsigned char array which holds the channel
>>> values in hex
>>> the frame is 10bit per channel, meaning the there are two bytes used
>>> for each channel - the byteorder is therefore not like in a normal
>>> rgb-array (r g b r g b)
>>> it is: rr gg bb

>>
>> This sounds a little odd. The usual meaning of "values in hex" is
>> that the values is stored using some character set to represent hex
>> digits. Thus two bytes == two characters == 2 hex digits == 8 bits so
>> string 10 bits is not possible.

>
> very true - two bytes are filled with the characters representing the
> hex digits

Only you know the format of your data, but a hex digit represents 4
bits so you can't represent 10 bits using two hex digits. I suspect
that you think the data is in hex because some software or system has
shown it to you like that. My guess would be that the data is just
encoded as a 10-bin binary integer in two consecutive bytes, but only
you can know for sure.

> thus the decoding function will be very neccessary (?)

If you really do have hex digits, yes. One thing I know -- you don't
need the rather odd function that was in your last post. That was
intended to something or other (I did not look in detail) with an IEEE
floating point number in memory.

There are loads of ways to write a hex digit-to-value conversion
function. My inclination would be to write:

int value_of_hex_digit(int c)
{
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return c - '0';
case 'A': case 'a':
return 10;
case 'B': case 'b':
return 11;
case 'C': case 'c':
return 12;
case 'D': case 'd':
return 13;
case 'E': case 'e':
return 14;
case 'F': case 'f':
return 15;
}
/* you can indicate an error here if that helps or is required */
return 0;
}

> also i have allocated memory for *floatframe and i simply forgot
> to post it - same with the return type - sorry for that
>
> here is the complete code snippet (decode_ieee_single is in the post above):

It is much better to post a small, complete program that illustrates
the problem. Often, just trying to write one shows you where you have
gone wrong.

> double* convert_frame_to_float(unsigned char* frame, unsigned long int
> size){
>
> int j = 0;
> double* floatframe;
>
> if((floatframe = (double *) malloc(size)) == NULL){

The size is wrong. If there are size/2 doubles going to be stored (as
it looks like there are from the loop below, you need what I wrote in
my last post. You don't need the cast and you *must* say how big a
double is: malloc(size/2 * sizeof *floatframe).

> exit(1);
> }
> memset(floatframe, 0.0, (size));

Don't do this. You don't need it and what you have written is wrong
in several ways.

> //leaving out *tmp_frame
> for(int i=0; i<size; i+=2){
> unsigned long upper = (unsigned long)frame;
> unsigned long lower = (unsigned long)frame[i+1];

No need to cast. In fact you don't need these two variables at all.
>
> floatframe[j] = 256.0 * decode_ieee_single(upper,1) +
> decode_ieee_single(lower,1);

either:

floatframe[j] = 256.0 * value_of_hex_digit(frame) +
value)of_hex_digit(frame[i+1]);

or (if my guess is right):

floatframe[j] = 256.0 * frame) + frame[i+1];

> j++;
> }
> return floatframe;
> }

PS. Please snip more when you post.

--
Ben.

Ben Bacarisse, Jun 8, 2007
5. ### Ben BacarisseGuest

CBFalconer <> writes:

> stanley wrote:
>>
>> i've digged this out because i have a similar issue
>>
>> i have a rgb-frame in an unsigned char array which holds the
>> channel values in hex the frame is 10bit per channel, meaning the
>> there are two bytes used for each channel - the byteorder is
>> therefore not like in a normal rgb-array
>> (r g b r g b)
>> it is: rr gg bb
>>
>> my idea was to read out the two bytes of each channel an convert
>> them with the posted code, combine(add) them afterwards, while
>> multiplying the upper part with 256.
>>
>> any idea if this will work?

>
> Not the slightest. This is all off-topic for c.l.c. Go to a
> newsgroup that deals with your system.

What an extraordinary notion. Manipulating pixel arrays (which is
what the poster is asking about) can be done entirely in standard C.

--
Ben.

Ben Bacarisse, Jun 8, 2007
6. ### Ian CollinsGuest

CBFalconer wrote:
> stanley wrote:
>> i've digged this out because i have a similar issue
>>
>> i have a rgb-frame in an unsigned char array which holds the
>> channel values in hex the frame is 10bit per channel, meaning the
>> there are two bytes used for each channel - the byteorder is
>> therefore not like in a normal rgb-array
>> (r g b r g b)
>> it is: rr gg bb
>>
>> my idea was to read out the two bytes of each channel an convert
>> them with the posted code, combine(add) them afterwards, while
>> multiplying the upper part with 256.
>>
>> any idea if this will work?

>
> Not the slightest. This is all off-topic for c.l.c. Go to a
> newsgroup that deals with your system.
>

How so?

--
Ian Collins.

Ian Collins, Jun 8, 2007
7. ### Barry SchwarzGuest

On Thu, 07 Jun 2007 21:31:39 +0200, stanley
<> wrote:

>
>
>Ben Bacarisse wrote:
>> stanley <> writes:
>>
>>> i've digged this out because i have a similar issue
>>>
>>> i have a rgb-frame in an unsigned char array which holds the channel
>>> values in hex
>>> the frame is 10bit per channel, meaning the there are two bytes used
>>> for each channel - the byteorder is therefore not like in a normal
>>> rgb-array (r g b r g b)
>>> it is: rr gg bb

>>
>> This sounds a little odd. The usual meaning of "values in hex" is
>> that the values is stored using some character set to represent hex
>> digits. Thus two bytes == two characters == 2 hex digits == 8 bits so
>> string 10 bits is not possible.

>
>very true - two bytes are filled with the characters representing the
>hex digits
>
>thus the decoding function will be very neccessary (?)
>also i have allocated memory for *floatframe and i simply forgot
>to post it - same with the return type - sorry for that
>
>here is the complete code snippet (decode_ieee_single is in the post above):

In the "post above", you define decode_ieee_single as taking a void*
and an int. In this code, you pass it a long and an int. I think you
need to get out of retyping mode and start using cut and paste.

>
>
Code (Text):

>double* convert_frame_to_float(unsigned char* frame, unsigned long int
>size){
>
>int j = 0;
>double* floatframe;
>
>if((floatframe = (double *) malloc(size)) == NULL){[/color]

Lose the cast.  Its only purpose is to allow you to invoke undefined
behavior without a diagnostic.

You allocate size bytes, not room for size doubles.
[color=blue]
>   exit(1);[/color]

Eliminate a portability barrier by using a standard value, such as
EXIT_FAILURE.
[color=blue]
>}
>memset(floatframe, 0.0, (size));[/color]

This will set the allocated storage to all bits zero.  If you wanted
to do that, you could just as easily use calloc instead of malloc.
However, there is no guarantee that all bits zero has any particular
meaning for a double.  Furthermore, since you assign values to all the
elements of floatframe below, the initialization is pointless.
[color=blue]
>//leaving out *tmp_frame
>for(int i=0; i<size; i+=2){
>   unsigned long upper = (unsigned long)frame[i];
>   unsigned long lower = (unsigned long)frame[i+1];
>
>   floatframe[j] = 256.0 * decode_ieee_single(upper,1) +
>decode_ieee_single(lower,1);
>   j++;[/color]

Unless you have one of those rare systems where sizeof(double) is 2 or
less, you have just invoked undefined behavior.  j will run from 0 to
size/2, perhaps size/2-1.  Since you only allocated size bytes for
floatframe, you will rapidly reach the point where floatframe[j]
doesn't exit (i.e., is beyond the end of the allocated memory).
[color=blue]
>}
>return floatframe;
>}[/color]

Remove del for email

Barry Schwarz, Jun 8, 2007
8. ### stanleyGuest

Ben Bacarisse wrote:
[snip]
> Only you know the format of your data, but a hex digit represents 4
> bits so you can't represent 10 bits using two hex digits.[...] but only
> you can know for sure.

yup - ive constructed the data^^
in fact its two channels of two char-coded hex-digits each
so thats 16bit holding 10bit - but that only for clarification

[snip]
> It is much better to post a small, complete program that illustrates
> the problem. Often, just trying to write one shows you where you have
> gone wrong.

i'll keep that in mind

> You don't need the cast and you *must* say how big a
> double is: malloc(size/2 * sizeof *floatframe).

[snip]
> or (if my guess is right):
> floatframe[j] = 256.0 * frame) + frame[i+1];

yes - your guess was right - and also my size is now allocated correctly
(very new to me since im mainly in c++ ...)

thank you very much for your help !

stanley, Jun 8, 2007
9. ### Keith ThompsonGuest

stanley <> writes:
> Ben Bacarisse wrote:
> [snip]
>> Only you know the format of your data, but a hex digit represents 4
>> bits so you can't represent 10 bits using two hex digits.[...] but only
>> you can know for sure.

> yup - ive constructed the data^^
> in fact its two channels of two char-coded hex-digits each
> so thats 16bit holding 10bit - but that only for clarification

Using a hexadecimal encoding, 16 bits (assuming that's 2 characters
representing 2 hexadecimal digits) can represent only 8 bits of data.
For example, the hexadecimal number FF represents the binary number
11111111.

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Keith Thompson, Jun 8, 2007
10. ### stanleyGuest

Barry Schwarz wrote:
[snip]
>> for(int i=0; i<size; i+=2){

[snip]
>> j++;

[snip]
> Since you only allocated size bytes for
> floatframe, you will rapidly reach the point where floatframe[j]
> doesn't exit (i.e., is beyond the end of the allocated memory).

yes - that was the problem - ben already pointed it out.

thanks anyways,
stanley

stanley, Jun 8, 2007
11. ### stanleyGuest

Keith Thompson wrote:
> stanley <> writes:

[snip]
>> in fact its two channels of two char-coded hex-digits each
>> so thats 16bit holding 10bit - but that only for clarification

>
> Using a hexadecimal encoding, 16 bits (assuming that's 2 characters
> representing 2 hexadecimal digits) can represent only 8 bits of data.
> For example, the hexadecimal number FF represents the binary number
> 11111111.

its one charactor representing 2 hex-digits

greetz,
stanley

stanley, Jun 8, 2007
12. ### Ben BacarisseGuest

CBFalconer <> writes:

> Ben Bacarisse wrote:
>>

> ... snip ...
>>
>> There are loads of ways to write a hex digit-to-value conversion
>> function. My inclination would be to write:
>>
>> int value_of_hex_digit(int c)
>> {
>> switch (c) {
>> case '0': case '1': case '2': case '3': case '4':
>> case '5': case '6': case '7': case '8': case '9':
>> return c - '0';
>> case 'A': case 'a':
>> return 10;
>> case 'B': case 'b':
>> return 11;
>> case 'C': case 'c':
>> return 12;
>> case 'D': case 'd':
>> return 13;
>> case 'E': case 'e':
>> return 14;
>> case 'F': case 'f':
>> return 15;
>> }
>> /* you can indicate an error here if that helps or is required */
>> return 0;
>> }

>
> Your comment is true enough, but I consider your code excessive.
> Try:
>
> int hexvalue(char c) {
> static const char hexv[] = "0123456789abcdefABCDEF";
> char *ptr;
>
> if (!(ptr = strchr(hexv, c))) return -1;
> else if (ptr < &hexv[16]) return ptr - &hexv[0];
> else return ptr - &hexv[0] - 6;
> } /* untested as typed */

[syntax error corrected in case anyone else wants to cut-and-paste]

A strchr-based solution came up last time this was discussed so I
decided to go a different route. Variety is the spice of usenet. The
one posted then was something like this:

static char hexv[] = "0123456789abcdef";
char *cp = strchr(hexv, tolower(c));
return cp ? cp - hexv : 0;

which, to my, eye has the edge. My apologies, but I can't remember
whose it was.

--
Ben.

Ben Bacarisse, Jun 8, 2007
13. ### Keith ThompsonGuest

stanley <> writes:
> Keith Thompson wrote:
>> stanley <> writes:

> [snip]
>>> in fact its two channels of two char-coded hex-digits each
>>> so thats 16bit holding 10bit - but that only for clarification

>> Using a hexadecimal encoding, 16 bits (assuming that's 2 characters
>> representing 2 hexadecimal digits) can represent only 8 bits of data.
>> For example, the hexadecimal number FF represents the binary number
>> 11111111.

>
> its one charactor representing 2 hex-digits

That doesn't make much sense. A single hex digit represents one of 16
different values, so it effectively contains 4 bits of information,
but it's held in a character, which is at least 8 bits. If you have
an 8-bit character holding 2 groupings of 4 bits, then that's not

The term hexadecimal refers to a *textual* representation of binary
data, using the characters '0'..'9' and 'a'..'f' (or 'A'..'F'). If
you're not representing the data as those characters, you don't have
hexadecimal (even if some tool happens to display the data that way).

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Keith Thompson, Jun 8, 2007
14. ### stanleyGuest

Keith Thompson wrote:
[snip]
>> its one charactor representing 2 hex-digits

>
> That doesn't make much sense. A single hex digit represents one of 16
> different values, so it effectively contains 4 bits of information,
> but it's held in a character, which is at least 8 bits. If you have
> an 8-bit character holding 2 groupings of 4 bits, then that's not

[snip]

thanks for the input - maybe i've just chosen the wrong term

now i know better and i'll try to keep that in mind

cheers,
stanley

stanley, Jun 8, 2007