reinterpret_cast - to interpret double as long

S

Suzanne Vogel

I'd like to convert a double to a binary representation. I can use the
"&" bit operation with a bit mask to convert *non* float types to binary
representations, but I can't use "&" on doubles.

To get around this limitation on double, I'd like to keep the bits of
the double the *same* but change its interpretation to long. I can use
"&" on longs. I tried to use reinterpret_cast for this purpose, but it
returned zero every time.

double n1 = 32;
long n2 = *(reinterpret_cast<long*>(&n)); // returns zero, for all n1

*** How can I keep the bits of the double the *same* but change its
interpretation to long? What am I doing wrong?

Thanks,
Suzanne
 
V

Victor Bazarov

Suzanne Vogel said:
I'd like to convert a double to a binary representation. I can use the
"&" bit operation with a bit mask to convert *non* float types to binary
representations, but I can't use "&" on doubles.

To get around this limitation on double, I'd like to keep the bits of
the double the *same* but change its interpretation to long. I can use
"&" on longs. I tried to use reinterpret_cast for this purpose, but it
returned zero every time.

double n1 = 32;
long n2 = *(reinterpret_cast<long*>(&n)); // returns zero, for all n1

*** How can I keep the bits of the double the *same* but change its
interpretation to long? What am I doing wrong?

On your platform 'double' is likely longer than 'long'. And
your hardware is likely little-endian. '32' as a double has
zero mantissa. That means that only the UPPER two bytes have
some non-zero bits in them. The lower six bytes are all zeroes
for any double that is a power of two. When you ask for the
address of the double, you get the address of the lowest byte.
Reinterpreting it as an address of a long gives you four lower
bytes. They have no bits set in them.

You need at least two longs to see the bit pattern in a double
on your platform. Try doing this (non-portable!):

double d1 = 32;
unsigned long *plong = reinterpret_cast<unsigned long*>(&d1);
unsigned long n1 = plong[0], n2 = plong[1];

and see what you get in n1 and n2. (I used unsigned long because
they are better for bit patterns)

Once again: this code has nothing to do with standard C++. It
produces _undefined_behaviour_. Whatever it happens to do on
your platform is not what it should do on any other platform
(or even with a different version of the same compiler).

Victor
 
J

John Harrison

Suzanne Vogel said:
I'd like to convert a double to a binary representation. I can use the
"&" bit operation with a bit mask to convert *non* float types to binary
representations, but I can't use "&" on doubles.

To get around this limitation on double, I'd like to keep the bits of
the double the *same* but change its interpretation to long. I can use
"&" on longs. I tried to use reinterpret_cast for this purpose, but it
returned zero every time.

double n1 = 32;
long n2 = *(reinterpret_cast<long*>(&n)); // returns zero, for all n1

*** How can I keep the bits of the double the *same* but change its
interpretation to long? What am I doing wrong?

Its pretty unlikely that long is the same size as double. I would guess that
you are only seeing half your double, and that half is all zero.

You could try this

double n1 = 32;
long n2 = *(reinterpret_cast<long*>(&n));
long n3 = *(reinterpret_cast<long*>(&n) + 1);

But a more reliable method would be to use a union

union
{
double d;
char c[sizeof(double)];
} conv;

conv.d = 32;
// print out double in hex
for (int i =0; i < sizeof(double); ++i
cout << hex << setfill('0') << setw(2) << conv.c;

Thanks,
Suzanne

john
 
I

Ioannis Vranos

Suzanne Vogel said:
I'd like to convert a double to a binary representation.


It already has one. Do you mean you want to see the 1s and 0s?


I can use the
"&" bit operation with a bit mask to convert *non* float types to binary
representations, but I can't use "&" on doubles.

To get around this limitation on double, I'd like to keep the bits of
the double the *same* but change its interpretation to long. I can use
"&" on longs. I tried to use reinterpret_cast for this purpose, but it
returned zero every time.

double n1 = 32;
long n2 = *(reinterpret_cast<long*>(&n)); // returns zero, for all n1

*** How can I keep the bits of the double the *same* but change its
interpretation to long? What am I doing wrong?


Better tel us exactly what you want to do, rather than describe it.




--
Ioannis

* Programming pages: http://www.noicys.freeurl.com
* Alternative URL 1: http://run.to/noicys
* Alternative URL 2: http://www.noicys.cjb.net
 
S

Suzanne Vogel

Thanks, all. Victor's solution worked. Indeed, a double on my machine is
twice as big as a long.

Yes, what I meant by my question was that I wanted to see the
machine-dependent 0's and 1's that a double consisted of.

I encapsulated the conversion in a class. Instead of assuming that a
double is twice as big (or whatever) as a long on *everyone*'s machine,
I use sizeof() to test.

FINDING (perhaps of interest to the "-0 vs. 0" folks):
For doubles, my machine uses signed-bit representation (i.e., a 1-bit to
represent the sign), with the exception that it represents 0 and -0 the
same way. The compiler actually compiles -0 as 0, which I can see by
setting a breakpoint at "double x = -0" and noting that the disassembly
code does the assignment by XOR-ing a register with itself to the give
value 0 (*not* -0).

(All the question marks below are because I can't delete my array
without getting an exception. But that's kind of irrelevant to this thread.)

/******************************************************************************
* Class to reinterpret the bits of 'value' of type 'From' to type 'To'.
* i.e., Keep the bits the same, but reinterpret the value.
* This gives the system-dependent binary representation.
******************************************************************************/
template<class To, class From>
class ReinterpretCast {
private:
std::string representation;
public:
ReinterpretCast() {
// no-op
}
ReinterpretCast(From value) {
int sizeOfOutput = (sizeof(From)>sizeof(To)) ?
(sizeof(From)/sizeof(To)) : 1;
To* buffer = new To[sizeOfOutput];
buffer = reinterpret_cast<To*>(&value);
for (int i=0; i<sizeOfOutput; i++) {
representation += toBinary<To>(buffer); // toBinary() is
a function I wrote, which gives the machine-dependent 0's and 1's
}
//delete[] buffer; // ??????????????????????????????????????????
}
ReinterpretCast(const ReinterpretCast<class To, class From>& rhs) {
representation = rhs.representation;
}
~ReinterpretCast() {
// no-op
}
std::string getRepresentation() const {
return representation;
}
int operator==(const ReinterpretCast& rhs) const {
return typeid(this)==typeid(&rhs) &&
representation==rhs.representation;
}
};

Then, to see the binary representation of double(32.0), I can do:
std::cout << "double: " << ReinterpretCast<long, double>
(double(32.0)).getRepresentation() << "\n";

The whole mess is posted here:
http://www.cs.unc.edu/~vogel/playpen/c++/std/BaseAndBitReps/Main.cpp
I'll spare you by not pasting any more of the code into this message.

Suzanne
On your platform 'double' is likely longer than 'long'. And
your hardware is likely little-endian. '32' as a double has
zero mantissa. That means that only the UPPER two bytes have
some non-zero bits in them. The lower six bytes are all zeroes
for any double that is a power of two. When you ask for the
address of the double, you get the address of the lowest byte.
Reinterpreting it as an address of a long gives you four lower
bytes. They have no bits set in them.

You need at least two longs to see the bit pattern in a double
on your platform. Try doing this (non-portable!):

double d1 = 32;
unsigned long *plong = reinterpret_cast<unsigned long*>(&d1);
unsigned long n1 = plong[0], n2 = plong[1];

and see what you get in n1 and n2. (I used unsigned long because
they are better for bit patterns)

Once again: this code has nothing to do with standard C++. It
produces _undefined_behaviour_. Whatever it happens to do on
your platform is not what it should do on any other platform
(or even with a different version of the same compiler).

Victor
 
V

Victor Bazarov

Suzanne Vogel said:
Thanks, all. Victor's solution worked. Indeed, a double on my machine is
twice as big as a long.

Yes, what I meant by my question was that I wanted to see the
machine-dependent 0's and 1's that a double consisted of.

I encapsulated the conversion in a class. Instead of assuming that a
double is twice as big (or whatever) as a long on *everyone*'s machine,
I use sizeof() to test.

FINDING (perhaps of interest to the "-0 vs. 0" folks):
For doubles, my machine uses signed-bit representation (i.e., a 1-bit to
represent the sign), with the exception that it represents 0 and -0 the
same way. The compiler actually compiles -0 as 0, which I can see by
setting a breakpoint at "double x = -0" and noting that the disassembly
code does the assignment by XOR-ing a register with itself to the give
value 0 (*not* -0).

The compiler will NOT create code for -0 when compiling "-0"
expression. -0 is an integer expression resulting in (int)0.
What you need is to somehow flip that sign bit in the double
value. Not guaranteed to be printable, anyway.

On your system:

double a = 0.0;
(All the question marks below are because I can't delete my array
without getting an exception. But that's kind of irrelevant to this thread.)
[...]
ReinterpretCast(From value) {
int sizeOfOutput = (sizeof(From)>sizeof(To)) ?
(sizeof(From)/sizeof(To)) : 1;
To* buffer = new To[sizeOfOutput];

Why do you think you need to allocate anything here?
buffer = reinterpret_cast<To*>(&value);

So, you allocate 'buffer' and then immediately change its value
to point to something else. How do you expect to 'delete' it?
It loses the value you obtained from 'new' here. A memory leak.
for (int i=0; i<sizeOfOutput; i++) {
representation += toBinary<To>(buffer); // toBinary() is
a function I wrote, which gives the machine-dependent 0's and 1's
}
//delete[] buffer; // ??????????????????????????????????????????


Drop the 'new', you don't need it.

I decided not to comment on the rest of it.

Victor
 
S

Suzanne Vogel

Why do you think you need to allocate anything here?
So, you allocate 'buffer' and then immediately change its value
to point to something else. How do you expect to 'delete' it?

Oops, I see that now.
I decided not to comment on the rest of it.

However, the comment "I decided not to comment on the rest of it" made
me suspect that my code sucked, so I've been refactoring it. Even if
that was not the intended meaning, well, my code still sucked. I split
up the files, got rid of a couple of functions, renamed a couple of things.
Thanks,
Suzanne
 
I

Ioannis Vranos

Suzanne Vogel said:
Oops, I see that now.


However, the comment "I decided not to comment on the rest of it" made
me suspect that my code sucked, so I've been refactoring it. Even if
that was not the intended meaning, well, my code still sucked. I split
up the files, got rid of a couple of functions, renamed a couple of
things.


Why don't you deal with the double as a sequence of unsigned chars?




--
Ioannis

* Programming pages: http://www.noicys.freeurl.com
* Alternative URL 1: http://run.to/noicys
* Alternative URL 2: http://www.noicys.cjb.net
 
S

Suzanne Vogel

Victor or someone,

*** What does this line do? i.e., How does it translate into the format
"[l-value] = [something]"?
*(reinterpret_cast<char*>(&a) + sizeof(a) - 1) ^= 0x80;

I thought it would do the following, but it doesn't:
a = (*(reinterpret_cast<char*>(&minusZero) + sizeof(minusZero)-1) ^
0x80);

*** Why are you using "sizeof(a)-1"?

I'm sorry, but this particular syntax and bit formatting is foreign to
me. Thanks.

Suzanne
 
V

Victor Bazarov

Suzanne Vogel said:
Victor or someone,

*** What does this line do? i.e., How does it translate into the format
"[l-value] = [something]"?
*(reinterpret_cast<char*>(&a) + sizeof(a) - 1) ^= 0x80;

If P is a char*, then P + N is also a char*. *(P + N) is a char&
(a reference to a char).
I thought it would do the following, but it doesn't:
a = (*(reinterpret_cast<char*>(&minusZero) + sizeof(minusZero)-1) ^
0x80);

*** Why are you using "sizeof(a)-1"?

That's the _last_, the most significant, byte of 'a' (if your
system is little-endian). What else would I use?
I'm sorry, but this particular syntax and bit formatting is foreign to
me. Thanks.

OK, you could write

char *pa = reinterpret_cast<char*>(&a) + sizeof(a) - 1; // address
char &ra = *pa; // get a reference from the address
ra = ra ^ 0x80; // flip the bit in the char using the reference.

I just wrote it in one statement. (Of course this works only on
a system that is little-endian and has 8-bit chars)

I supposed you need to read about compound assignment operators
as well. Have you ever seen a += b? If yes, why a ^= b looks
so foreign to you? If not, well, open your textbook...
 
S

Sean

Suzanne Vogel said:
Victor or someone,

*** What does this line do? i.e., How does it translate into the format
"[l-value] = [something]"?
*(reinterpret_cast<char*>(&a) + sizeof(a) - 1) ^= 0x80;

Hi Suzanne,

Sometimes it's easier to understand if you break it
down into multiple statements instead:

char* tmp1 = reinterpret_cast<char *>(&a);
tmp1 = tmp1 + sizeof(a) - 1;
(*tmp1) ^= 0x80;

Here's a small program to play around with this:

#include<iostream>
using std::cout;
using std::hex;
using std::endl;

int main()
{
cout << hex; // Set output to hex formatting

{
unsigned int a = 0xa5a5a5a5;
*(reinterpret_cast<char*>(&a) + sizeof(a) - 1) ^= 0x80;
cout << a << endl; // Prints: 25a5a5a5
}

{
unsigned int a = 0xa5a5a5a5;
char* tmp1 = reinterpret_cast<char *>(&a);
tmp1 = tmp1 + sizeof(a) - 1;
*tmp1 ^= 0x80;
cout << a << endl; // Prints: 25a5a5a5
}

}

HTH,
Sean
 
S

Suzanne Vogel

Thanks for the additional comments.

Victor said:
*** What does this line do? i.e., How does it translate into the format
"[l-value] = [something]"?
*(reinterpret_cast<char*>(&a) + sizeof(a) - 1) ^= 0x80;

If P is a char*, then P + N is also a char*. *(P + N) is a char&
(a reference to a char).

Yes... But here it seems we have *(char* + int), not *(char* + int*):
*(reinterpret_cast<char*>(&a) + sizeof(a)-1) ^= 0x80;
If I could see how we *do* have *(char* + int*), then I could understand
why adding a mere value of "sizeof(a) - 1" accomplishes a bit shift (see
below).

** Why does it accomplish a bit shift??????
That's the _last_, the most significant, byte of 'a' (if your
system is little-endian). What else would I use?

I was/am confused why you are adding the mere value "sizeof(a)-1". This
seems to be accomplishing a bit shift of "(sizeof(a)-1)" bytes (bytes,
not bits!) left, because of what I said above.
OK, you could write

char *pa = reinterpret_cast<char*>(&a) + sizeof(a) - 1; // address
char &ra = *pa; // get a reference from the address
ra = ra ^ 0x80; // flip the bit in the char using the reference.

I just wrote it in one statement. (Of course this works only on
a system that is little-endian and has 8-bit chars)

I supposed you need to read about compound assignment operators
as well. Have you ever seen a += b? If yes, why a ^= b looks
so foreign to you?

Yes, of course I've seen "a += b". That's in piddly Java, my 1st piddly
language. I've just never seen "(a + c) += b", which is more similar to
your "(*(reinterpret_cast<char*>(&a) + sizeof(a)-1) ^= 0x80)". (a + c)
is not an l-value, so I didn't see how (*(reinterpret_cast<char*>(&a) +
sizeof(a)-1) could be an l-value. Seems this is one more rule that is
different for pointers than for primitives.

** Why is (*(reinterpret_cast<char*>(&a) + sizeof(a)-1) an l-value,
allowed to be used on the LHS of an assignment?????

I took a guess, thinking that one would unroll "(a + c) += b" (if it
were legal, which it's *not*) as "a = (a + c) + b" (where the pluses
don't necessarily have to be the same operator). So, I unrolled
"(*(reinterpret_cast<char*>(&a) + sizeof(a)-1) ^= 0x80)" as "a =
If not, well, open your textbook...

If only I knew what to look up. :) It's all about knowing the buzz
words, the Google terms, the jargon. I've got "The C++ Programming
Language" by Bjarne Stroustrup, but I haven't seen anything more than
the basic "a += b" in that.

I ask questions only when I don't know what to look up, or when I'm
confused by what I've looked up. Having been a teaching assistant, I
understand the annoyance of having students ask questions unprepared.

I'll get it eventually...

Suzanne
 
I

Ioannis Vranos

Suzanne Vogel said:
Thanks for the suggestion, but this prints out gibberish (8 Å @) on my
computer. And adding static_cast<unsigned char> within the loop didn't
help -- and it shouldn't, because p is already an unsigned char.

Instead of printing each char p out straight as it is, I have to do
bitwise-AND with a bit mask to extract the bits one by one. (And I use
long, instead of char, although that might be unnecessary.)



With C++ you can avoid the dangerous low level stuff and play more safely.
Check this:


#include <iostream>
#include <bitset>
#include <limits>


int main()
{
using namespace std;

double d=999;

unsigned char *p=reinterpret_cast<unsigned char *>(&d);

// There are systems where 1 byte is more than 8 bits
bitset<numeric_limits<unsigned char>::digits> byte;

for(unsigned i=0; i<sizeof(d); ++i)
{
byte=p;

for(unsigned j=0; j<byte.size(); ++j)
cout<<byte;
}

cout<<endl;
}


It does what you want elegantly and efficiently.




--
Ioannis

* Programming pages: http://www.noicys.freeurl.com
* Alternative URL 1: http://run.to/noicys
* Alternative URL 2: http://www.noicys.cjb.net
 
I

Ioannis Vranos

Ioannis Vranos said:
Suzanne Vogel said:
Thanks for the suggestion, but this prints out gibberish (8 Å @) on my
computer. And adding static_cast<unsigned char> within the loop didn't
help -- and it shouldn't, because p is already an unsigned char.

Instead of printing each char p out straight as it is, I have to do
bitwise-AND with a bit mask to extract the bits one by one. (And I use
long, instead of char, although that might be unnecessary.)



With C++ you can avoid the dangerous low level stuff and play more safely.
Check this:


#include <iostream>
#include <bitset>
#include <limits>


int main()
{
using namespace std;

double d=999;

unsigned char *p=reinterpret_cast<unsigned char *>(&d);

// There are systems where 1 byte is more than 8 bits
bitset<numeric_limits<unsigned char>::digits> byte;

for(unsigned i=0; i<sizeof(d); ++i)
{
byte=p;

for(unsigned j=0; j<byte.size(); ++j)
cout<<byte;



The above line should be: cout<<byte[j];
 
S

Suzanne Vogel

Thanks, Ioannis! This also answers another question of mine: How to get
the number of bits per byte.

Suzanne
 
S

Suzanne Vogel

Thanks, Victor. I think understand now, and I do like the one-liner
below. I wasn't consciously inattentive (although I don't think I'm
stupid); I was simply not in the pointer mindset and hence I was
closed-minded, *and* I forgot that bit shifting can occur only within a
single byte.

double a = 0.0;
*(reinterpret_cast<char*>(&a) + sizeof(a)-1) ^= 0x80;

Here's what I've learned/realized (so you realize your posts were
worthwhile):
- 0x80 == 10000000 == a byte with 1 at the left and all the rest 0's
- bit-shifting can occur only *within* a byte; hence 0x80 << [something]
will give all zeros and not accomplish anything
- adding (sizeof(a)-1) moves that many bytes left (on my system); this
is what I meant by "bit shifting" (but "bit shifting" is the wrong
terminology, since I'm not actually *changing* anything in memory)
- *(a+b) is an l-value, because it's the value referenced by address a+b

So, putting all this together, the line above does the following (on my
system, and where I rewrite pa == reinterpret_cast<char*>(&a)):
000000000000000000000000000000000000000000000000000000000000000000000000
| |
+-(pa + sizeof(a)-1) +-pa

100000000000000000000000000000000000000000000000000000000000000000000000
| |
+-*((pa + sizeof(a)-1) ^ 0x80)-----------------------------------------+

If this is correct, I don't know why I didn't see it sooner; if this is
*not* correct, then I must be crazy.....
......
Do you understand the annoyance having to repeat oneself simply
because of inattentiveness of the listener? :)

Yep, sure do.
Good luck!

Thanks. :)
Suzanne
 

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

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top