A question about the i386 double type memory layout

Programming, for all ages and all languages.
Post Reply
skan
Posts: 3
Joined: Wed Apr 04, 2007 2:05 pm
Location: Atlanta, Ga

A question about the i386 double type memory layout

Post by skan »

Hi everyone:

First a little about me. I have been reading these forums for quite some time (>2 years I think). Until now have never had a reason to post. I started a hobby os once a while back. It progressed to the point of booting (with GRUB's help) and writing text to the screen with vga memory. Perhaps some time in the future I will have time to work on it again.
Now to my question. I am implementing a c library for communicating in a binary udp protocol. The protocol relies on sending double precision floating point numbers. It specifies it's own standard for their transmittal. The line in the documentation is
Floating point values are IEEE doubles stored in the representation as on the i386.
So that would be IEEE doubles stored with the least significant 32bits of the significant first and then the sign, exponent and rest of the significant in the second 32bits. Could someone either correct me or confirm that as fact?

Thank you in advance,
Skan
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

In this context i'd assume that's correct.

However i think the documentation would be better off specifying the byte order directly instead of this strange wording. Which in this case would be little endian/intel byteorder.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

I do notice you are using double precision and not single, but this post might help you understand the format since I am assuming that you may not since doubles might have to same format?

http://www.osdev.org/phpBB2/viewtopic.p ... ting+point
skan
Posts: 3
Joined: Wed Apr 04, 2007 2:05 pm
Location: Atlanta, Ga

Post by skan »

I know the double format. I had to be able to read some simple ones manually when I took computer architecture for my CS degree. This was really more of a question if I had the endian-ness correct for the i386. Also I agree that the wording in the documentation is wierd. Whoever wrote this should be smacked upside the head for their (lack of) good documentation. Espesally when an error in their code has a possibility of physically hurting someone. However you work with what you have.
Thank you for your timely answers.

-Skan
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

I misunderstood you. I am sorry. The actual representation in memory can be demonstrated by:

Code: Select all

double c = 5.5;
unsigned char *b = (unsigned char*)&c;

printf("%x.%x.%x.%x\n", b[0], b[1], b[2], b[3]);
printf("%x.%x.%x.%x\n", b[4], b[5], b[6], b[7]);
The most significant bits come first in each byte which is demonstrated by:

Code: Select all

unsigned char v = 0xff;
printf("First Value:%u Second Value:%u\n", v, v>>1);
So a thirty-two bit value stored as 0x12345678 is really 0x78,0x56,0x34,0x12 in memory on the I386. Where each byte's bit are
from least significant to most significant. The only reason the individual bytes are reversed is because the processor stored it as 0x1E and displayed it in big endian as 0x78.
7........8................1......E..................7.......8
0111 1000 ----> 0001 1110 ------> 0111 1000

I see it in binary as:
....5.....6.......7......8
0101 0110 0111 1000 = 0x5678

The Intel sees it as:
0001 1110 0110 1010 = 0x1E6A
If I try to read just one byte of it which the Intel will see as:
0001 1110 = 0x1E
It will reverse it to:
0111 1000 = 0x78

It just reverses the eight bits for a byte, sixteen bits for a word, and thirty-two bits for a double word.

We read in big endian and the processor reads in little endian.

5413413 to the processor is really 3143145.

:oops:
So that would be IEEE doubles stored with the least significant 32bits of the significant first and then the sign, exponent and rest of the significant in the second 32bits. Could someone either correct me or confirm that as fact?
If I serialized the data of the four bytes starting at the lowest bit address. It would be serialized as.... mantissa, exponent, sign..

0x1234 would truly be viewed as 0001->0010->0011->0100, but stored as 0010->1100->0100->0001 in each nibble in memory moving upwards on the Intel.

If I am correct when a PCI card has a logic one on the address pins. Which go from A0 to A31. Then highest bit of the address would be stored at pin A31 and represent the decimal value 2,147,483,648.

It just depends on how you are viewing it.

"Floating point values are IEEE doubles stored in the representation as on the i386."
skan
Posts: 3
Joined: Wed Apr 04, 2007 2:05 pm
Location: Atlanta, Ga

Post by skan »

sorry I came across a little snippy in my last post. didnt mean to.
All this is getting pretty complicated. I develop on a ppc platform but the actual processor is xscale which is an embeded version of x86 from what I understand. So im stuck in cross hardware purgatory for a while. Hopefully I will get this all straightened out eventually. Thank you for your help.

-Skan
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

Are you creating a UDP packet and trying to place a double precision float in it and have the i386 based processor get the packet and read the float?

I have never developed anything on the PPC, but you could try something as simple as this to convert a eight byte value from little endian to big endian.

Code: Select all

inline void storeInBigEndian(double myDouble, void *to){
      (unsigned long*)to)[1] = htonl(((unsigned long*)&myDouble)[0]);
      (unsigned long*)to)[0] = htonl(((unsigned long*)&myDouble)[1]);
      return;
}
A PPC would mess up using that function, but a INTEL processor should reverse the byte order completely. So maybe that is what you need in the network library for transmitting double precision floats between the computers? You might have to select using that function or just a place holder when compiling on the PPC.

It will depend on what system you would like to take the performance hit? A INTEL already takes a performance hit in the TCP/IP network stack so you might make the PPC reverse the order, by replacing htonl with ntohl and making the INTEL do nothing to send it.

If that does not work try reversing all the bits. You should be able to figure it out with a little testing.

I read that the PPC and any Intel processor after the year 2000 should use the same floating point format. I suppose only the bytes would need to be reversed in order for one to understand the other. I also read that the PPC could switch from big endian to little endian?
User avatar
mystran
Member
Member
Posts: 670
Joined: Thu Mar 08, 2007 11:08 am

Post by mystran »

skan wrote:I know the double format. I had to be able to read some simple ones manually when I took computer architecture for my CS degree. This was really more of a question if I had the endian-ness correct for the i386. Also I agree that the wording in the documentation is wierd. Whoever wrote this should be smacked upside the head for their (lack of) good documentation. Espesally when an error in their code has a possibility of physically hurting someone. However you work with what you have.
Thank you for your timely answers.
Suggestion: write a small program, that stores a float, then reconstructs it (without using floating point arithmetics), based on IEEE format. Then you just check whether you need to byteswap to get them right.

IIRC i386 stores floats just like any IEEE processor, but to be honest I'm not sure about it. :)
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
Post Reply