Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
Solved

double bit manipulation

Posted on 2003-03-02
Medium Priority
463 Views
hey,

i need to seperate a double into its 32 bit low and high parts. and then pass those parts into doubles again.

im very ignorant of the standard C++ libraries so can somebody help me?

cheers,
simon
0
Question by:sheslop
• 5
• 4
• 2
• +3

LVL 48

Accepted Solution

AlexFM earned 800 total points
ID: 8055396
union DoubleParts
{
double d;
struct HL
{
unsigned int high;
unsigned int low;
} hl;
};

void DivideDouble(double d, unsigned int* high, unsigned int* low)
{
DoubleParts dp;
dp.d = d;

*high = dp.hl.high;
*low = dp.hl.low;
}

double MakeDouble(unsigned int high, unsigned int low)
{
DoubleParts dp;

dp.hl.high = high;
dp.hl.low = low;

return dp.d;
}

0

LVL 49

Expert Comment

ID: 8055859
AlexFM's solution is the best.  Other techniques would require two-layer type coersion which can be knotty to read.

However, I'd change the names to
SplitDouble()
and
StitchDouble()

Because "divide" has a mathematical meaning which could confuse the uninformed.
-- Dan

0

LVL 2

Expert Comment

ID: 8055915
This is the second time I've seen an answer like this.  Am I the only one who thinks that this type of answer makes architecture (endian) assumptions?  I would personally like to see an explanation more than a simple answer.

biran
0

LVL 49

Expert Comment

ID: 8056055
A union is stricltly defined:  It means that the same physical storage are is used to hold two or more data types.

AlexFM's code *does* make an assumption that sizeof(double) is equal to the size of the two adjacent unsigned ints.  However, endedness is not at issue at all because the two unsigned ints will never be examined or used for their numeric value.  They will just be vehicles for transporting some arbitrary bit patterns.

-- Dan
0

LVL 12

Expert Comment

ID: 8056242
brahmer,

There's absolutely NO POINT in trying to make this kind of conversion to be free of endianess assumptions. The point is that double - or floating point formats - are inherently machine specific. Yes, there are what you might call IEEE formats, but even that standard has room for differences so two machines might both have "IEEE" standard floats but they are still not binary compatible.

So unless you convert float to string (decimal or hex or something) there is no machine independent way to transfer floats. There is therefore no point in trying to make it independent of endianess to the machine. Each double format is inherently specific to the hardware platform you're running on.

There are exceptions to this rule, Ada has the possibility to specify machine independent floating point. This is done through emulation and bit manipulation. The data type double is by definition meant to be the floating point type that is supported by hardware - if such support exist - and will therefore vary from one machine to another.

The above code given works on any machine that has sizeof(double) == 2 * sizeof(int) and it works fine on other endianess as well. Provided you do not attempt to interpret each of those parts in any way. It might happen that what the code refer to "high" is actually the low part of the floating point value but that just means that "high" and "low" changes meaning. When you later combine them back to double they work correctly in either case.

You should - from the above - never attempt to combine them on a machine with different hardware than the original machine which split the double value up. If you do the split on a PC you also do the combine on a PC and so it works fine. IF you do the same on a motorola what you call "high" is actually the "low" part etc but when you combine them again they are again combined correctly, provided you combine them on a Motorola CPU.

Attempting to split on a PC and then combine them on a motorola or vice versa wouldn't make sense in any case so that would just be silly.

Alf
0

LVL 8

Expert Comment

ID: 8056705
>> union DoubleParts
>> {
>>    double d;
>>    struct HL
>>    {
>>        unsigned int high;
>>        unsigned int low;
>>    } hl;
>> };

Why can't you say,

union DoubleParts
{
double d;
unsigned int parts[2];
};

Exceter
0

LVL 12

Expert Comment

ID: 8057339
Exceter,

of course you can. You just write parts[0] and parts[1] instead of the high and low. Last time I checked there was no law against using arrays in this situation.

Alf
0

LVL 49

Expert Comment

ID: 8061008
Using the array would also have another benefit -- not needing to assume that the two separate ints are next to each other or that there is no unexpected padding between them.  For instance, you could add:

ASSERT( sizeof(double) == sizeof(dp.parts) );

-- Dan
0

LVL 2

Expert Comment

ID: 8061026
Alf, thanks for that very good description.  The main point that two systems don't need to be binary-compatible completely slipped by me.  My brain must have been misplaced.  :)  (i knew that part)
The other case I was thinking of was when someone asked how to convert a 32-bit value to 16-bit values.  I automatically thought of using shift and and/or logic because it implies exactly what I'm trying to do.

Exceter, there's reasons for both side.  On one side, i think one is making an endian-statement, but that also produces, IMO, more readable code (but I would ensure that my high and low were correct terms.)

I wrote an example for the int/short case:

#include <stdio.h>

union Chunks
{
unsigned int i;
unsigned short s[2];
};

int main(int argc, char *argv[])
{
union Chunks foo;
foo.i = 0x10000;
printf("First chunk: %d, second chunk: %d\n", foo.s[0], foo.s[1]);
return 0;
}
Solaris SPARC output this:
First chunk: 1, second chunk: 0
Windows XP, AMD Athlon output this:
First chunk: 0, second chunk: 1

The reason I wouldn't do it this way is because I write a lot of code that is network-based, uses binary files that need to be cross-platform compatible, etc.  Therefore, I always want to know what each byte means exactly.

Is there any practical reason why this question comes up?  I've never had to do anything of the sort.  Of course, I don't do much with math or sci applications.

brian
0

LVL 49

Expert Comment

ID: 8061410
If you check  sheslop's other open question, you can get an idea of what this is all about.   sheslop is having trouble passing a double from VB to a VC DLL and back and is probaly exploring the possibility of passing two UINTs instead.

sheslop, are you listening?

-- Dan
0

LVL 12

Expert Comment

ID: 8062959
He cannot pass uints....VB doesn't have unsigned types.

He can try to pass integer type though (signed).

Another thing he could explore is the use of Currency type, this type is 64 bit as far as I know, but it is also specific to VB. In C you would consider it a struct that contain an integer and a scale value.

The C# decimal type is implemented the same way except it is 128 bits and uses a 32 bit scale factor and a 96 bit integer part.

The decimal type essentialy replaces the old VB currency type so it wouldn't surprise me if VB.Net has the currency type implemented exactly like the C# decimal type. But VB6 should use the old 64 bit currency type and could be used for double.

Doesn't VB have double already? If you pass the double as arg to a C function, what do you get on the C side? I would expect a C double value, is that wrong?

Alf
0

LVL 49

Expert Comment

ID: 8063047
>>If you pass the double as arg to a C function, what do you get on the C side?

Salte,
I think that once again, you have drilled down into the core -- the very essense of the problem.  I'll bet that you could solve both of sheslop's problems if you answered that one question.

-- Dan
0

LVL 12

Expert Comment

ID: 8063315
Not so easy for me to answer since it is his program on his platform. Yes, I also have a PC running windows at home but I don't have VB installed on it...

However, if he told us exactly what he received on the C side when he from VB passes a double, I am sure we (all the experts on this forum) can figure out how to get it straight :-)

I will imagine one of two things:

1. VB double is exactly the same as the C and C++ double data type. If so, there's no conversion needed.

2. VB uses some odd double data type. Unless it is emulated the only data type I can think of is the 80 bit floating point data type that the math processor in Intel architecture supports, if so the type double won't even fit in a 64 bit int but must be declared as a struct on the C side. Since parameters usually have to be multiple of 4 the 10 byte data type isn't really doing that so it is often extended to 12 bytes where two of the bytes are not used.

struct VBdouble {
unsigned char v[12];
};

would then be an appropriate argument. It is possible that VB transfer this by address so you get a pointer to this struct as argument or it can transfer the value by value in which case you should use the struct as the argument in the C and C++ side.

Either way, if this is the case then that format is very clearly defined and specified by Intel and you can by bit manipulation convert it to a double. Of course, you have to sacrifice some bits and it's even possible the value cannot be represented by double since double is only 64 bits.

Somehow I doubt that VB would go for that 80 bit float type though - it is usually considered historic. Modern languages use it only for internal calculations, as soon as you store values in memory or in variables they are converted to double.

The third option - which involves emulation - is that VB uses some weird 64 bit double type which follows its own rules. Again, I believe this isn't very likely since the only such emulated format in widespread use on PCs was the old Microsoft format and MS no longer support the conversion functions because the format is no longer used by them and hasn't been supported for many years. If VB used that format I am pretty sure C and C++ would have extensive support to convert to/from that type to regular double.

So most likely the data type you get in the other end is a double and so you don't need any conversion. What might be a problem then is how the different languages transfer floating point values as arguments to functions. As you perhaps know, the intel platform has from old days used a math coprocessor with its own small stack with room for 8 values, this is used so that an instruction can load a value from memory into the math processor and you can then use it in calculations and also keep it in the stack if you use the same value several times in the same calculation. As long as you don't use more than 8 values in the stack that's fine.

The point is that many languages transfer floating point values in that very 8 slot stack while some languages transfer floating point values on the regular stack in memory.

Similarly the return value from a function - if floating point - might be transferred by the value being placed on top of the 8 slot stack in the processor and then the function returns. The caller expect the value at the top of that co-processor stack to be return value from the function.

Integer values are typically returned in the EAX register (AX if short and AL if char).

So there's a difference between floating point and integers in the way parameters are transferred and what exactly those differences are might differ from one language to another.

I don't know how VB internally transfer variables to C functions but I would expect it did it in a way that C or C++ is likely to expect and so providing a double as argument to a function really shouldn't require any conversion at all:

(I must admit that VB isn't my strongest language, so forgive me if I provide some wrong syntax here)

function func(arg as double) return double

In C or C++:

extern "C" {
double __stdcall func(double arg);
};

I believe something like this should work perfectly and no conversion would be needed. The double arg is used exactly as the type in VB.

Alf
0

Author Comment

ID: 8100898
thanks for all the input people.

after i posted the question a new method was devised to solve our problem that didn't require the use of a C++ function. (did whole thing in SQL).

sorry i didn't accept an answer earlier but was too busy.

AlexFm's answer would have been what i was after.

thanks.
0

Featured Post

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â€¦
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilationâ€¦
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 viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Suggested Courses
Course of the Month13 days, 3 hours left to enroll