Link to home
Start Free TrialLog in
Avatar of Francoz
Francoz

asked on

Why this binary file code is not working>

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.
Avatar of Aleph
Aleph

try:
cout << "Float value with byte addition\t" << readreal << endl;
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.
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
Avatar of Mayank S
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.
ASKER CERTIFIED SOLUTION
Avatar of TriG
TriG

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Francoz

ASKER

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.
> 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
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
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
> 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.
Avatar of Francoz

ASKER

Hi everyone I feel the main qn is answered now. thanx trig,aleph,mayankeagle  and all.
>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
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
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.
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