?
Solved

Why this binary file code is not working>

Posted on 2003-03-30
15
Medium Priority
?
314 Views
Last Modified: 2012-05-05
void main
{
     struct ibm
     {
          char startbit;
          float salary;
          char name[10];
          char stopbit;
     };

     ibm ibm1 = {'t',885.34,"abcdefghi",'f'};
     
     fstream File("c:\\sample.bin", ios::out |  ios::in | ios::binary);
        File.write((char *)(&ibm1), sizeof(ibm));

        float readreal = 0.0;
     File.seekg(sizeof(char) ,ios::beg);
     File.read((char*)&readreal,sizeof(float));
     cout << "Float value with byte addition\t" << readreal;
}
I'm not getting the value 885.34 printed. Why?
I've few more questions, but let's see this one first.
0
Comment
Question by:Francoz
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 5
  • 2
  • +2
15 Comments
 
LVL 2

Expert Comment

by:Aleph
ID: 8236993
try:
cout << "Float value with byte addition\t" << readreal << endl;
0
 
LVL 2

Expert Comment

by:Aleph
ID: 8237017
By the way, that struct is probably not packed. On most computers, 4-byte values must be aligned to addresses that are a multiple of 4, and since the whole struct probably is aligned in such a way, 3 bytes of padding will be inserted after the first char.
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8237056
As a side note, this isn't a valid c++ program to start with. The application entry point isn't defined and your linker should complain about this. Use this function signature instead:

int main( int argc, char* argv[] )

Apart from that, Aleph hit the nail. I would also think that it is an alignmet issue. To check this, calculate sizeof( ibm ) and compare it to sizeof( char ) + sizeof( float ) + sizeof( char[10] ) + sizeof( char ). Those 2 values are likely not the same.

.f
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 30

Expert Comment

by:Mayank S
ID: 8237269
Well, there are often many unexpected issues involved with the storage of floating-point variables. I once happened to try the folowing code:

float a = 0.6 ;

if ( a < 0.6 )
  cout << "\n Lesser. " ; // end if
 
else
   cout << "\n Greater or equal. " ; // end else

a = 0.7 ;

if ( a < 0.7 )
  cout << "\n Lesser. " ; // end if
 
else
  cout << "\n Greater or equal. " ; // end else


And the output I got was:

Greater or equal.
Lesser.


There is often a loss of precision in storing floating-point values, which is why I never use them now. I always prefer using 'double'. In fact, even in the above code, when I changed the data-type of 'a' to 'double', the output was:

Greater or equal.
Greater or equal.

And on another testing, after writing 35.67 (stored in a floating-point variable) to a file, when I read it, I got 35.668294! I changed the data-type to 'double', and my problem was solved.

Just thought I'll let you know.... might be of some help to you.

Mayank.

PS:
>> float readreal = 0.0;
>> File.seekg(sizeof(char) ,ios::beg);
>> File.read((char*)&readreal,sizeof(float));
>> cout << "Float value with byte addition\t" << readreal;

Try using a structure variable to read it:

ibm temp ;
File.seekg ( 0, ios :: beg ) ;
File.read ( ( char * ) &temp, sizeof ( ibm ) ) ;

There are few other errors with your code too (like the missing parethesis () after 'main', etc) but I guess you must've corrected them by now.

Mayank.
0
 
LVL 1

Accepted Solution

by:
TriG earned 60 total points
ID: 8237353

ibm ibm1 = {'t',885.34,"abcdefghi",'f'};

This assignment is fine... regardless of alignment.  Note that there is an implicit cast from double to float (the literal 885.34 is a double if you don't put an f afterwards)  Anyways, that's OK too.


But as  mayankeagle has shown, the reading code is wrong cuz of the packing.  On a PC using vc++ the char will take up 4 bytes (3 bytes of padding).

so
File.seekg(sizeof(char) ,ios::beg);

should read
File.seekg(4 ,ios::beg);

Usually you can give hints to the compiler as to how to pack.  See docs for

#pragma pack... and
__declspec(align(#))


Also, beware that the file generated is not necessarily portable to other platforms as the endianness (the order of bytes that represent numbers) may be different.
0
 
LVL 2

Author Comment

by:Francoz
ID: 8237436
Actually I wanted result why the seekg() func is not able to give me the correct location of pointer. I feel Trig has answered correctly. I made change to the func as follows

File.seekg(sizeof(char)+3 ,ios::beg);
//The size 3 is added for padding

Now it works correctly. And also to eliminate the warning we can give floating point assignment as

real = 0.0F;

Hey trig can you tell me something more on padding.
One interesting result is if I give float as the first element of struct the file size is 16bytes. Otherwise it is 20bytes.
0
 
LVL 2

Expert Comment

by:Aleph
ID: 8237695
> One interesting result is if I give float as the first element of struct the file size is 16bytes. Otherwise it is 20bytes.

That is because, as I said before, the float, which is a four byte field, must be stored at an address in the memory that is an even multiple of four. Since your whole struct is stored at such a location, that is fine when the float is the first field in the struct. But when you have a char as the first field, three bytes of padding is inserted by the compiler in order to align the float to a valid address.

0x1230 | char | pad  | pad  | pad  |
0x1234 |          float            |
0x1238 | char | char | char | char |
...

makes it 20 bytes, wheras

0x1230 |          float            |
0x1234 | char | char | char | char |
0x1238 | char | char | char | char |
...

makes it 16 bytes
0
 
LVL 1

Expert Comment

by:TriG
ID: 8238362
That's it.

These alignment issues are for improved efficiency only.  Non aligned floats etc must work... but on older processors they might run slower (there's barely a difference with, say, a new P4)

i.e.  This should work fine ( assuming the new returned a 4 byte aligned mem location... which it will always do, but isn't required to).

   ibm ibm1 = {'t',885.34f,"abcdefghi",'f'};
   char* space = new char[ sizeof(ibm) + 1 ];
   ibm* pIBM = (ibm*)(space+1);
   *pIBM = ibm1;

   fstream File("c:\\sample.bin", ios::out |  ios::in | ios::binary);
   File.write((char *)(pIBM), sizeof(ibm));

So should multiplying unaligned floats etc.  Some assembly instructions require alignment (eg. SSE instructions) and can be capitalized on if things are tidy.  That's not to say alignment isn't required in all sorts of cases (DMA's, ethernet drivers, etc., etc.) but we're only dealing with the language, not an API.

--tri
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8238414
TriG: "there's barely a difference with, say, a new P4"

Erm, why shouldn't it be a problem there? It most definately is more of a problem on newer cpu's than on those old ones, where memory access bottlenecks weren't that much of a hassle, due to the simple design of the MCH. Sure, if your cpu runs on 2GHz it will feel like there isn't a problem when comparing it to a 386 33MHz -- it is, however, in relation a lot more of a penalty.

.f
0
 
LVL 2

Expert Comment

by:Aleph
ID: 8238528
> These alignment issues are for improved efficiency only.

Not really. Many processors require that types are aligned in order to work with them. If you try to access a field that isn't aligned you'll get a bus error. I'm not sure about the specifics of the x86 architecture -- perhaps it can handle unaligned fields since it's a CISC processor -- but for example a Sun SPARC processor can't.

You can however instruct the compiler to pack structures, but that only means it will produce extra code to work around the problem, unless I'm mistaken.
0
 
LVL 2

Author Comment

by:Francoz
ID: 8238931
Hi everyone I feel the main qn is answered now. thanx trig,aleph,mayankeagle  and all.
0
 
LVL 1

Expert Comment

by:TriG
ID: 8241344
>These alignment issues are for improved efficiency only.

I should have emphasized the word "These" more.  Yes, there are many cases where alignment is required, but a language abstracts those details... That's why ANSI C, for example, doesn't guarantee anything about how things get packed in a struct; it needs freedom to accomodate the hardware.

The unaligned code I submitted must work, even on a SPARC... as you said, the compiler may have to produce more code to work around the problem, thus making it less efficient.

--tri
0
 
LVL 1

Expert Comment

by:TriG
ID: 8241647
fl0yd,

>"there's barely a difference with, say, a new P4"

Yeah, I shouldn't have said that.  Alignment is very important for efficiency.

I've been doing math code using SIMD instructions and was noticing how the latencies for the unaligned versus aligned stores and loads are near identical.

MOVAPS Load->4  Store->4
MOVUPS Load->4  Store->5

but I shouldn't have implied that, in general, it doesn't matter much.

--tri
0
 
LVL 2

Expert Comment

by:Aleph
ID: 8253498
TriG,

No, it won't work. Running this on a SunBlade 100:

  struct S { Float f; };
  S s;
  s.f = 0.0;
  char *space = new char[sizeof(S) + 1];
  S* sp = (S*) (space + 1);
  *sp = s; // Error here

gives this error:

  Bus Error

Bucause *sp cannot be dereferenced as a four byte type while not aligned.
0
 
LVL 1

Expert Comment

by:TriG
ID: 8257577
Hmm,

Well, there are two ways to go here :)

A) Either admit that the code shouldn't be portable, or

B) Claim that your SunBlade 100 should have implemented chars as 32bits each if it can't deal with the lack of aligment.

Note: sizeof(char) should equal to 1 even on a 4byte per character machine... which, according to the God Stoustrup 3rd edition p76, do in fact exist.

I suppose the question is, did the compiler on the Sun opt not to support the Language exactly, which, if the language requires the above to work, is the only reasonable thing to do (who would want 4byte characters?)

OR

Does C++ to allow the above to be considered non-portable?

Still holding on by a thread :)

--tri


0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
Suggested Courses

765 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question