converting floating point number to byte array in C

S

ssubbarayan

Hi all,
I am trying a program to convert floating point values to a byte array
and printing the same to the screen.The idea behind this is we already
have an existing function which can do byte level parsing what ever
may be the type of data.The data would be coming from an external
environment.When I parse int,char at byte level,I get right
values,where as floating point just prints 0.000000 to the
screen.Given below is a sample program I tried:

#include <stdio.h>
struct info
{
unsigned char day;
unsigned char month;
unsigned short year;
unsigned char arr[6];
float data;
};
struct info info1;
struct info info2;
void parser(unsigned char* data);

int main(int argc, char *argv[])
{
info1.day=31;
info1.month=8;
info1.year=2016;
info1.arr[0]='h';
info1.arr[1]= 'a';
info1.arr[2]= 'i';
info1.data=12345.50;
printf("data is %f\n",info1.data);
printf("size of info struct is %d\n",sizeof(info));
parser((unsigned char*)(&info1));

return 1;
}
void parser(unsigned char* data)
{
int i;
info2.day=data[0];
info2.month=data[1];
info2.year=(data[3]<<8)|(data[2]);
#if 0
for(i=0;i<10;i++)
{
printf("subsequent value in array is %d
\n",data);
}

#endif
for(i=4;data!='\0';i++)
{
printf("character in arr is %c\n",data);
}
printf("data in arr is %c\n",data[4]);
printf("data in arr is %c
\n",data[5]);
printf("data in arr is %c\n",data[6]);
info2.data=(float)((data[10]<<24)|(data[9]<<16)|(data[8]<<8)|
(data[7]));
printf("day is %d\n",info2.day);
printf("month is %d\n",info2.month);
printf("year is %d\n",info2.year);
printf("data is %f\n",info2.data);
}

I get correct values for all the variables with in info2 struct except
for info2.data.
info2.data,prints 0.000000 in the screen,where as assigned value is
12345.50.

Where have I done mistake?I tried searching in net and this groups,but
nothing seems explainative enough for me to understand.
Can someone help me to correct this and give me a sample program
showing how to convert floating point values to bytes in C?Any links
are also appreciated.

Looking farward for your replies and advanced thanks for the same,
Regards,
s.subbarayan
 
J

Jens Thoms Toerring

ssubbarayan said:
I am trying a program to convert floating point values to a byte array
and printing the same to the screen.The idea behind this is we already
have an existing function which can do byte level parsing what ever
may be the type of data.The data would be coming from an external
environment.When I parse int,char at byte level,I get right
values,where as floating point just prints 0.000000 to the
screen.Given below is a sample program I tried:
#include <stdio.h>
struct info
{
unsigned char day;
unsigned char month;
unsigned short year;
unsigned char arr[6];
float data;
};
struct info info1;
struct info info2;
void parser(unsigned char* data);
int main(int argc, char *argv[])
{
info1.day=31;
info1.month=8;
info1.year=2016;
info1.arr[0]='h';
info1.arr[1]= 'a';
info1.arr[2]= 'i';
info1.data=12345.50;
printf("data is %f\n",info1.data);
printf("size of info struct is %d\n",sizeof(info));

This should be

printf("size of info struct is %d\n",sizeof(struct info));

or

printf("size of info struct is %d\n",sizeof info1 );

or it won't compile.
parser((unsigned char*)(&info1));
return 1;
}
void parser(unsigned char* data)
{
int i;
info2.day=data[0];
info2.month=data[1];
info2.year=(data[3]<<8)|(data[2]);
#if 0
for(i=0;i<10;i++)
{
printf("subsequent value in array is %d
\n",data);
}

#endif
for(i=4;data!='\0';i++)
{
printf("character in arr is %c\n",data);
}
printf("data in arr is %c\n",data[4]);
printf("data in arr is %c
\n",data[5]);
printf("data in arr is %c\n",data[6]);
info2.data=(float)((data[10]<<24)|(data[9]<<16)|(data[8]<<8)|
(data[7]));
printf("day is %d\n",info2.day);
printf("month is %d\n",info2.month);
printf("year is %d\n",info2.year);
printf("data is %f\n",info2.data);
}

I get correct values for all the variables with in info2 struct except
for info2.data.
info2.data,prints 0.000000 in the screen,where as assigned value is
12345.50.
Where have I done mistake?I tried searching in net and this groups,but
nothing seems explainative enough for me to understand.

There are several thinbs broken with your approach. First of all
you don't consider that the compiler could insert any amount of
padding bytes between the elements of the structure. That it
seem to work is no proof that it's correct - things can easily
stop to work when you use a different platform (or maybe just a
different compiler).

Second, if you really want to use external data instead data
generated by your program there's the problem with different
endianness and different sizes of types. So, unless you use
only data from a machine with the same architecture it also
will break for integer data types.

Another assumption you make is that all machines use the same
encoding for characters. While this is probably true for 99%
of all machines it is not a given.

Finally
info2.data=(float)((data[10]<<24)|(data[9]<<16)|(data[8]<<8)|(data[7]));

is really strange. Even if there would be no padding in the
structure etc. and you have 2 byte shorts then the data
element of the structure starts at byte 10 and uses this and
the following bytes, not the ones before.

Then what you're doing here is make an integer out of the bytes
and then store that integer value in a float variable - the cast
is completely redundant. If the data element of the structure
really starts at position 10 then you would have to use

info2.data = * ( float * ) ( data + 10 );

to get it right. This tells the compiler that 'data + 10' is
the address where a float is and to dereference this address
and store the value in info2.data.

Again, if you're using "external data", i.e. data generated on
a different machine you have a very good chance that this will
fail. Same for the case that the compiler had to insert padding
bytes.
Can someone help me to correct this and give me a sample program
showing how to convert floating point values to bytes in C?Any links
are also appreciated.

You can't reliably unless you know exactly all the details how
data are stored on the machine that created the data, how many
padding bytes the compiler inserts on that machine etc. Instead
of doing such dirty tricks (and probably getting it right only
for a small fraction of the possible cases better use a scheme
to store and read data in a unique format. Simplest thing is
probably to convert everything to ASCII strings and work with
those.

You may also have a look at RPC (Remote Procedure Call) and the
external data representation (XDR) used there to "serialize"
the data . But, as far as I remember, even that doesn't deal
with floating point numbers...

Regards, Jens
 
V

vippstar

In C89, a cast should be used
when one of the arguments to printf is a size_t type.

It is indeed so.
printf("size of info struct is %d\n", (int)sizeof info1 );

Don't use that.
or

printf("size of info struct is %u\n", (unsigned)sizeof info1 );

That's okay.
or

printf("size of info struct is %lu\n",
(long unsigned)sizeof info1 );

That's even better, since in C89, ULONG_MAX is the largest integer.

What I'd do

#if __STDC_VERSION__ == 199901L
# define x(z) z
# define PRIzINT "zu"
#else
# define x(z) (unsigned long)z
# define PRIzINT "lu"
#endif

size_t n;

/* ... */

printf("n = %" PRIzINT "\n", x(n));
 
A

Andy

Hi all,
I am trying a program to convert floating point values to a byte array
and printing the same to the screen.The idea behind this is we already snip
info2.data=(float)((data[10]<<24)|(data[9]<<16)|(data[8]<<8)|
(data[7]));
snip
I get correct values for all the variables with in info2 struct except
for info2.data.
info2.data,prints 0.000000 in the screen,where as assigned value is
12345.50.
A way to do it is
I:\c16>type ba1.c
#include <stdio.h>

int main() {
float f = 12345.5;
unsigned char *p = (unsigned char *)&f;
union {
float f;
unsigned char c[4];
} cf;

cf.c[0] = *p;
cf.c[1] = *(p + 1);
cf.c[2] = *(p + 2);
cf.c[3] = *(p + 3);

printf("%f\n", cf.f);

return 0;
}
I:\c16>tcc ba1.c
Turbo C Version 2.01 Copyright (c) 1987, 1988 Borland International
ba1.c:
Turbo Link Version 2.0 Copyright (c) 1987, 1988 Borland
International

Available memory 395354
I:\c16>ba1
12345.500000

I:\c16>
 
N

Nick Keighley

On 18 Aug, 16:26, (e-mail address removed) (Jens Thoms Toerring) wrote:

You may also have a look at RPC (Remote Procedure Call) and the
external data representation (XDR) used there to "serialize"
the data . But, as far as I remember, even that doesn't deal
with floating point numbers...

you remember incorrectly

http://www.faqs.org/rfcs/rfc1014.html

(RFC 1014 is XDR)

There is both a float and a double type
 
K

Keith Thompson

It is indeed so.


Don't use that.

It's not unreasonable to use int when you're sure the size is no more
than 32767 bytes.
That's okay.

Ok, that takes you up to 65535 bytes. Why is that significantly
better than 32767?
That's even better, since in C89, ULONG_MAX is the largest integer.

Yes, that's the most general C89/C90-compatible way to do it.
What I'd do

#if __STDC_VERSION__ == 199901L
# define x(z) z
# define PRIzINT "zu"
#else
# define x(z) (unsigned long)z
# define PRIzINT "lu"
#endif

size_t n;

/* ... */

printf("n = %" PRIzINT "\n", x(n));

First, I'd use
#if __STDC_VERSION__ >= 199901L
rather than
#if __STDC_VERSION__ == 199901L

And I wouldn't go to all that trouble unless I was seriously worried
about the size of that particular thing exceeding ULONG_MAX bytes.
The best C90 solution:

printf("The size is %lu\n", (unsigned long)sizeof whatever);

is also a perfectly reasonable C99 solution most of the time, and it
has the advantage of not forcing the reader to figure out what x() and
PRIzINT mean. (And in any case, I wouldn't call that macro "x"; I'd
at least pick an all-caps name.)
 
V

vippstar

It's not unreasonable to use int when you're sure the size is no more
than 32767 bytes.



Ok, that takes you up to 65535 bytes. Why is that significantly
better than 32767?

If the value of the sizeof expression is out of range, the cast will
invoke undefined behavior or implementation defined behavior. I don't
remember which, but I'm going to read some previous messages of mine
asking that question.

The best C90 solution:

printf("The size is %lu\n", (unsigned long)sizeof whatever);

is also a perfectly reasonable C99 solution most of the time, and it
has the advantage of not forcing the reader to figure out what x() and
PRIzINT mean. (And in any case, I wouldn't call that macro "x"; I'd
at least pick an all-caps name.)

I called it x just for the example ;)
 
K

Keith Thompson

If the value of the sizeof expression is out of range, the cast will
invoke undefined behavior or implementation defined behavior. I don't
remember which, but I'm going to read some previous messages of mine
asking that question.
[...]

Ah, yes, good point.

A conversion to a signed type yields an implementation-defined result
(or, in C99, may raise an implementation-defined signal) if the value
cannot be represented in the target type.

A conversion to an unsigned type yields a well defined result (though
if it's out of range it won't be particularly meaningful in this
case).

If you really want to be paranoid, you can write a routine to print a
size_t value; it can check whether the value will fit in an unsigned
long before attempting to print it. Or you can just write the code to
generate a decimal string representing the value. (I'm thinking of
the possibility of a partial C99 implementation that has size_t bigger
than unsigned long, but that doesn't support "%zu"; since the compiler
and runtime library are sometimes from different vendors, this isn't
entirely implausible.)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,038
Latest member
OrderProperKetocapsules

Latest Threads

Top