Re: Convert from unsigned char array to float!

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

  1. stanley

    stanley Guest

    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:
    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
    $ link 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
    #1
    1. Advertising

  2. 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
    #2
    1. Advertising

  3. stanley

    stanley Guest

    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:
    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;
    }
    
    thanks in advance,
    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
    #3
  4. 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
    #4
  5. 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
    #5
  6. stanley

    Ian Collins Guest

    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
    #6
  7. 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:
    >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
    #7
  8. stanley

    stanley Guest

    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
    #8
  9. 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
    #9
  10. stanley

    stanley Guest

    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
    #10
  11. stanley

    stanley Guest

    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
    #11
  12. 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
    #12
  13. 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
    hexadecimal, it's just binary.

    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
    #13
  14. stanley

    stanley Guest

    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
    > hexadecimal, it's just binary.

    [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
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. bd
    Replies:
    0
    Views:
    663
  2. Goran
    Replies:
    3
    Views:
    13,891
    tabin
    Jun 7, 2007
  3. Alex Vinokur
    Replies:
    9
    Views:
    832
    James Kanze
    Oct 13, 2008
  4. Gabriel Genellina
    Replies:
    0
    Views:
    524
    Gabriel Genellina
    Aug 13, 2009
  5. Carsten Fuchs
    Replies:
    45
    Views:
    1,648
    James Kanze
    Oct 8, 2009
Loading...

Share This Page