Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Unable to read the bits of a 'float' using char*

0 views
Skip to first unread message

Victor Freire

unread,
Aug 18, 2010, 12:50:21 AM8/18/10
to
Hello everyone,

I wrote these two functions for reading the bits of a float.

However, I can't figure out why the first method shown below works but
not the second one. I was sure the second one would also work but
that's not the case and I can't seem to find anything wrong with its
implementation.

I am rather puzzled with this. Could someone please shed some light on
this matter?


Thanks in advance,
Victor Freire

// Works OK
std::string getBits(const void* number, size_t sz)
{
unsigned *byte = (unsigned *)number;
std::string bits = "";

for(unsigned b = 0; b < (sz / 4); ++b) {
for(int i = 31; i >= 0; --i)
bits += (*byte & 1U << i) ? '1' : '0';
++byte;
}

return bits;
}

// Returns unexpected result... ???
std::string getBits(const void* number, size_t sz)
{
unsigned char *byte = (unsigned char*)number;
std::string bits = "";

for(unsigned b = 0; b < sz; ++b) {
for(int i = 7; i >= 0; --i)
bits += (*byte & ((unsigned char)(1) << i)) ? '1' : '0';
++byte;
}

return bits;
}


int main()
{
std::cout << "Bits: " << getBits(-12.75f, sizeof(float)); << "\n\n";
return 0;
}

*** RESULTS:
First one: 11000001010011000000000000000000 (CORRECT)
Second one: 00000000000000000100110011000001 (WRONG!)


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

red floyd

unread,
Aug 18, 2010, 2:39:31 PM8/18/10
to
On 8/17/2010 9:50 PM, Victor Freire wrote:
> Hello everyone,
>
> I wrote these two functions for reading the bits of a float.
>
> However, I can't figure out why the first method shown below works but
> not the second one. I was sure the second one would also work but
> that's not the case and I can't seem to find anything wrong with its
> implementation.
>
> I am rather puzzled with this. Could someone please shed some light on
> this matter?
>
>[redacted]

>
> *** RESULTS:
> First one: 11000001010011000000000000000000 (CORRECT)
> Second one: 00000000000000000100110011000001 (WRONG!)
>

Google for "Byte order".

Ian Collins

unread,
Aug 18, 2010, 2:33:38 PM8/18/10
to
On 08/18/10 04:50 PM, Victor Freire wrote:
> Hello everyone,
>
> I wrote these two functions for reading the bits of a float.
>
> However, I can't figure out why the first method shown below works but
> not the second one. I was sure the second one would also work but
> that's not the case and I can't seem to find anything wrong with its
> implementation.
>
> I am rather puzzled with this. Could someone please shed some light on
> this matter?

Byte order. The order of the bytes in a float does not match the
consecutive order of the bits. Break your output into groups of 8 and
you will see the pattern.

<snip>

> *** RESULTS:
> First one: 11000001010011000000000000000000 (CORRECT)
> Second one: 00000000000000000100110011000001 (WRONG!)

11000001 01001100 00000000 00000000
00000000 00000000 01001100 11000001

--
Ian Collins

Frank Buss

unread,
Aug 18, 2010, 2:34:48 PM8/18/10
to
Victor Freire wrote:

> // Works OK
> std::string getBits(const void* number, size_t sz)
> {
> unsigned *byte = (unsigned *)number;
> std::string bits = "";
>
> for(unsigned b = 0; b < (sz / 4); ++b) {
> for(int i = 31; i >= 0; --i)
> bits += (*byte & 1U << i) ? '1' : '0';
> ++byte;
> }
>
> return bits;
> }
>
>
>
> // Returns unexpected result... ???
> std::string getBits(const void* number, size_t sz)
> {
> unsigned char *byte = (unsigned char*)number;
> std::string bits = "";
>
> for(unsigned b = 0; b < sz; ++b) {
> for(int i = 7; i >= 0; --i)
> bits += (*byte & ((unsigned char)(1) << i)) ? '1' : '0';
> ++byte;
> }
>
> return bits;
> }
>
>
> int main()
> {
> std::cout << "Bits: " << getBits(-12.75f, sizeof(float)); << "\n\n";
> return 0;
> }

This doesn't compile, because of a syntax error in your cout line. But then
it doesn't compile with Visual Studio 2008, because of course, it can't
convert 'float' to 'const void*'. Below is a more working program, which
gives this output with Visual Studio:

Bits: 11000001010011000000000000000000
Bits: 00000000000000000100110011000001

The reason for the different result is byte ordering and depends on which
machine you compile it. Try this function on Intel machines:

string getBits2(float number, size_t sz)
{
unsigned char *byte = (unsigned char*) &number;
string bits = "";

for(int b = sz - 1; b >= 0; --b) {


for(int i = 7; i >= 0; --i)

bits += (byte[b] & ((unsigned char)(1) << i)) ? '1' : '0';
}

return bits;
}

Original code, errors corrected and slightly changed:

#include <string>
#include <iostream>

using namespace std;

// Works OK
string getBits(float number, size_t sz)
{
unsigned *byte = (unsigned *) &number;
string bits = "";

for(unsigned b = 0; b < (sz / 4); ++b) {
for(int i = 31; i >= 0; --i)
bits += (*byte & 1U << i) ? '1' : '0';
++byte;
}

return bits;
}

// Returns unexpected result... ???

string getBits2(float number, size_t sz)
{
unsigned char *byte = (unsigned char*) &number;
string bits = "";

for(unsigned b = 0; b < sz; ++b) {
for(int i = 7; i >= 0; --i)
bits += (*byte & ((unsigned char)(1) << i)) ? '1' : '0';
++byte;
}

return bits;
}


int main(int argc, char** argv)
{
cout << "Bits: " << getBits(-12.75f, sizeof(float)) << endl;
cout << "Bits: " << getBits2(-12.75f, sizeof(float)) << endl;
return 0;
}

--
Frank Buss, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de

Chris Morley

unread,
Aug 18, 2010, 2:37:38 PM8/18/10
to
> Hello everyone,
>
> I wrote these two functions for reading the bits of a float.
>
> However, I can't figure out why the first method shown below works but
> not the second one. I was sure the second one would also work but
> that's not the case and I can't seem to find anything wrong with its
> implementation.
>
> I am rather puzzled with this. Could someone please shed some light on
> this matter?
>
>
> Thanks in advance,
> Victor Freire

It is a byte order thing. Did these functions come from a Motorola machine?
getBits one (with unsigned int) fails on x86 with a non 32bit input.

Full example:

#include <string>
#include <typeinfo>

#include <stdio.h>

// Works OK
std::string getBits1(const void* number, size_t sz)


{
unsigned *byte = (unsigned *)number;
std::string bits = "";

for(unsigned b = 0; b < (sz / 4); ++b) {
for(int i = 31; i >= 0; --i)
bits += (*byte & 1U << i) ? '1' : '0';
++byte;
}

return bits;
}

// Returns unexpected result... ???

std::string getBits2(const void* number, size_t sz)


{
unsigned char *byte = (unsigned char*)number;
std::string bits = "";

for(unsigned b = 0; b < sz; ++b) {
for(int i = 7; i >= 0; --i)
bits += (*byte & ((unsigned char)(1) << i)) ? '1' : '0';
++byte;
}

return bits;
}

// Fixed on x86
std::string getBits3(const void* number, size_t sz) {


unsigned char *byte = (unsigned char*)number;

std::string bits;

for(int b = sz-1; b>=0; --b) {


for(int i = 7; i >= 0; --i)

bits += (byte[b] & ((unsigned char)(1) << i)) ? '1' : '0';
}

return bits;
}

template<class T> void t(const T* P) {
printf("%s\n", typeid(T).name() );
std::string s1 = getBits1(P, sizeof(T));
std::string s2 = getBits2(P, sizeof(T));
std::string s3 = getBits3(P, sizeof(T));
printf("1: %s\n", s1.c_str() );
printf("2: %s\n", s2.c_str() );
printf("3: %s\n", s3.c_str() );
printf("\n");
}

int main() {
float f = -65535.0f; t(&f);
double d = -65535.0; t(&d);
int i = 1; t(&i); long long li = 1; t(&li);

return 0;
}

--- 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ------ 8< ---

Running on cygwin on windows (x86) with gcc 4.3.4 I get

f
1: 11000111011111111111111100000000
2: 00000000111111110111111111000111
3: 11000111011111111111111100000000

d
1: 0000000000000000000000000000000011000000111011111111111111100000
2: 0000000000000000000000000000000011100000111111111110111111000000
3: 1100000011101111111111111110000000000000000000000000000000000000

i
1: 00000000000000000000000000000001
2: 00000001000000000000000000000000
3: 00000000000000000000000000000001

x
1: 0000000000000000000000000000000100000000000000000000000000000000
2: 0000000100000000000000000000000000000000000000000000000000000000
3: 0000000000000000000000000000000000000000000000000000000000000001

The long long is the most obvious showing the byte order you extract!

Chris

Václav Haisman

unread,
Aug 18, 2010, 2:46:59 PM8/18/10
to
On Aug 18, 6:50 am, Victor Freire wrote:
> Hello everyone,
>
> I wrote these two functions for reading the bits of a float.
>
> However, I can't figure out why the first method shown below works but
> not the second one. I was sure the second one would also work but
> that's not the case and I can't seem to find anything wrong with its
> implementation.
>
> I am rather puzzled with this. Could someone please shed some light on
> this matter?
>
>[...]
I am assuming you running this on x86 platform.

>
> // Works OK
> std::string getBits(const void* number, size_t sz)
> {
> unsigned *byte = (unsigned *)number;

Such type punning breaks the C++ aliasing rules. You cannot do this.
It is UB. Also the variable is misnamed. It obviously does not handle
bytes.

> std::string bits = "";
>
> for(unsigned b = 0; b < (sz / 4); ++b) {

You should use sizeof(unsigned) instead of magic number 4. You should
check that sz <= sizeof(unsigned), otherwise you are potentially doing
out of bounds access.

> for(int i = 31; i >= 0; --i)
> bits += (*byte & 1U << i) ? '1' : '0';
> ++byte;
> }
>
> return bits;
>
> }
>
> // Returns unexpected result... ???
> std::string getBits(const void* number, size_t sz)
> {
> unsigned char *byte = (unsigned char*)number;
> std::string bits = "";
>
> for(unsigned b = 0; b < sz; ++b) {
> for(int i = 7; i >= 0; --i)
> bits += (*byte & ((unsigned char)(1) << i)) ? '1' : '0';
> ++byte;
> }
>
> return bits;
>
> }
>
> int main()
> {
> std::cout << "Bits: " << getBits(-12.75f, sizeof(float)); << "\n\n";
> return 0;
>
> }
>
> *** RESULTS:
> First one: 11000001010011000000000000000000 (CORRECT)
> Second one: 00000000000000000100110011000001 (WRONG!)

Now, why does the first one "work" and the other does not?

The binary pattern of the float number, MSB first, looks like this:
11000001 01001100 00000000 00000000

x86 is little-endian platfrom so the least significant digits of the
binary number are at the lower addresses in memory. In this case the
bytes are:

number number+1 number+2 number+3
00000000 00000000 01001100 11000001

The second getBits() initializes "bytes" with pointer to "number", so
your loop goes from "number", to "number+1", to "number+2", to "number
+3", which results into the "00000000000000000100110011000001" string.
The function prints highest bit of the least significant byte, then
second highest bit of the least significant byte, etc., and finally
the lowest bit of the most significant byte.

OTOH the first getBits() grabs all of the 4 bytes at once and thus
prints the bits from the highest (31st) bit to the lowest (0th) bit
"correctly".

--
VH

Jerry Stuckle

unread,
Aug 18, 2010, 2:49:22 PM8/18/10
to

Both are correct. What you're running into is "little endian" - the way
numbers are stored on Intel and compatible machines (see
http://en.wikipedia.org/wiki/Endianness).

When you cast to an unsigned int, the compiler is compensating for the
little endian-ness and handling the number as one 4 byte value, so you
get the integer equivalent. But when you cast to unsigned char, the
value is handled as 4 separate bytes, so you get the internal
representation of the value in storage.

--
==================
Remove the "x" from my email address
Jerry Stuckle
JDS Computer Training Corp.
jstu...@attglobal.net
==================

ThosRTanner

unread,
Aug 18, 2010, 2:41:57 PM8/18/10
to

Edit results with spaces added every 8 digits makes it a lot clearer:

First one....: 11000001 01001100 00000000 00000000 (CORRECT)
Second one: 00000000 00000000 01001100 11000001 (WRONG!)

Looks extremely like you are operating on a little endian machine.

However, this begs the question:

WHY?

Also, you should not be using C style casts. use reinterpret_cast<>
instead.

danadam

unread,
Aug 18, 2010, 2:44:48 PM8/18/10
to
On Aug 18, 6:50 am, Victor Freire <victorlunafre...@gmail.com> wrote:
> Hello everyone,
>
> I wrote these two functions for reading the bits of a float.
>
> [...]

> *** RESULTS:
> First one: 11000001010011000000000000000000 (CORRECT)
> Second one: 00000000000000000100110011000001 (WRONG!)
>

The second version also works, you just didn't take into account
endianness of your machine. Lets group those bits:
1st: 11000001 01001100 00000000 00000000
2nd: 00000000 00000000 01001100 11000001
As you can see the groups are the same, just in reverse order.

Your cpu is probably intel or amd which are little endian. It means
that the least significant *bytes* take lower memory address.

IC

unread,
Aug 18, 2010, 2:49:47 PM8/18/10
to

Splitting your results into bytes:

11000001 01001100 00000000 00000000
00000000 00000000 01001100 11000001

Notice how the second has the same four bytes as the first, but with
the byte order reversed?

This is due to endianness. http://en.wikipedia.org/wiki/Endianness.

The second implementation depends on the order in which bytes are
stored within a word. You appear to be on a little-endian system. On a
big-endian system you'd get consistent results. The first
implementation doesn't depend on endianness as it doesn't address
individual bytes.

Also, not convinced by your passing a float constant as a void *
parameter. Does that really compile?

Cheers
IC

SG

unread,
Aug 18, 2010, 2:48:11 PM8/18/10
to
On 18 Aug., 06:50, Victor Freire wrote:
>
> I wrote these two functions for reading the bits of a float.
>
> However, I can't figure out why the first method shown below works but
> not the second one. I was sure the second one would also work but
> that's not the case and I can't seem to find anything wrong with its
> implementation.

The first implementation has an aliasing issue (I think). You are not
supposed to access a float object via an int lvalue. That's formally
undefined behaviour.

But the issue here is that there is an discrepancy between how you
think your machine works and how it actually works. The keyword is big
endian vs little endian.

> *** RESULTS:

> First one: 11000001 01001100 00000000 00000000 (CORRECT)
Bits 31-24 23-16 15-8 7-0

> Second one: 00000000 00000000 01001100 11000001 (WRONG!)

Byte 1st 2nd 3st 4th

Obviously, you're working on a little endian machine. So, the first
byte stores bits 7-0 of an int, the 2nd byte stores bite 15-8 of an
int, etc. Note that the C++ standard does not specify how the bits of
an unsigned int are broken into bytes. On other machines this might be
different.

As for the aliasing issue, you might want to use memcpy to be on the
safe side, that is:

float f = 3.14159265f;
unsigned u;
assert(sizeof(f)==sizeof(u));
memcpy(&u,&f,sizeof(u));

Cheers!
SG

Bo Persson

unread,
Aug 18, 2010, 2:52:46 PM8/18/10
to
Victor Freire wrote:
> Hello everyone,
>
> I wrote these two functions for reading the bits of a float.
>
> However, I can't figure out why the first method shown below works
> but not the second one. I was sure the second one would also work
> but that's not the case and I can't seem to find anything wrong
> with its implementation.
>
>
>
> *** RESULTS:
> First one: 11000001010011000000000000000000 (CORRECT)
> Second one: 00000000000000000100110011000001 (WRONG!)

It depends on the order of the bytes stored in memory. Which byte is
stored first?


Bo Persson

Pedro Lamarão

unread,
Aug 18, 2010, 2:50:31 PM8/18/10
to
On 18 ago, 01:50, Victor Freire <victorlunafre...@gmail.com> wrote:

> *** RESULTS:
> First one: 11000001010011000000000000000000 (CORRECT)
> Second one: 00000000000000000100110011000001 (WRONG!)

Did you notice the second answer gets every eight bit group backwards?

Look at the first eight bit group in your first answer and the last
eight bit group in your second answer.

--
P.

0 new messages