views:

492

answers:

9

I am trying to print out each bit of a floating point number in c.

I can be able to do it for integer with this:

int bit_return(int a, int loc)
// bit returned at location
{
  int buf = a & 1<<loc;

  if (buf == 0)
    return 0;
  else
    return 1;
}

the compiler wouldn't compile if I replace int a with float a.

Does anybody have a solution for this?

Thanks much.


Edit
Copy and reformat your coment below
Hope I didn't mess it up

Ok, for people who are not clear, i post my whole code here:

#include <stdio.h>
#include <stdlib.h>
int bit_return(int a, int loc) // bit returned at location
{
  int buf = a & 1<<loc;
  if (buf == 0) return 0;
  else return 1;
}

int main()
{
  int a = 289642; // represent a in binary
  int i = 0;
  for (i = 31; i>=0; i--)
  {
    printf("%d",bit_return(a,i));
  }
  return 0;
}
+4  A: 

Cast the address of your float to the address of an int of the same size, and pass that int to your existing function.

Pascal Cuoq
... But don't be surprised if within your compiler's strict-aliasing optimization approach the resulting code will simply print some irrelevant garbage.
AndreyT
Pascal Cuoq
Yay for unions.
Carl Norum
AndreyT where would an alias, as opposed to a cast, come into play? Seems like nowhere.
Heath Hunnicutt
Pascal Cuoq
Strict-aliasing rules essntially state that an object of type `T` cannnot be ever accessed through a pointer of type `U*`, where `T` and `U` are incompatible types. Compiler use this rule to resolve aliasing to better optimize the code. In this case it might "work", but in general tricks like this don't work.
AndreyT
@Pascal - the behavior described in your link is not a conforming C compiler. I realize we are talking about gcc. The reordering behavior described as "strict aliasing" should really be called "incorrect code generation." People who aren't using gcc will never have this problem.
Heath Hunnicutt
The behavior described in the link is a perfectly conforming behavior. It is a pity that this behavior was not introduced much earlier to help people develop good C programming habits. I'd say that about 50% of the crappy C code out there is due to people's lack of real C skills and their insistence on sticking to those pidgin C ideas they for some reason think is somehow related to C language. Were it introduced much earlier, I'd say we'd have about 50% less crappy so-called "C code" than we observe today.
AndreyT
@AndreyT I agree that this behavior should have been introduced earlier, because it would have moved C faster on its course towards irrelevance. Such undefined behaviors, implemented as traps by smart alec compiler developers, make it a pain to use for embedded/kernel programming. And you only need a glance at the generated assembly to see that C will never be good for generating fast code even with strict aliasing, because it still misses the guarantees of higher-level language. The sooner it's replaced by D or Ada for the first task, any modern programming language for the second, the better
Pascal Cuoq
Uhh, don't take that typical childish rant by Linus at all seriously. gcc wouldn't implement the optimization exactly the way he wanted it. He always had and has the choice of turning aliasing opt off but he doesn't want to do that and he doesn't want to write conforming code. And, of course, he called the gcc developers "stupid", "morons", and "insane", which anyone else in the world would have known might preclude the possibility of future cooperation. gcc is doing the right thing.
DigitalRoss
C does have escapes from strict aliasing and they do conform with the historical use of the language where `char *` is used for deliberate aliasing, as is obviously `union`. I agree with AndreyT, and the gcc implementation team. Even Linus basically agrees, the *only* reason he is upset is because he *does* want the optimization, he just wants them to water it down until the non-conforming Linux conforms to a weaker-than-C99 gcc/linus model. He just doesn't have any way to express himself other than by using insulting terms and *ad hominem* agruments.
DigitalRoss
+2  A: 

In C language, the term "bit" refers to an element of binary positional representation of a number. Integral numbers in C use binary positional representation, which is why they have "bits". These are the bits you "see" by means of bitwise operators (logical and shifts). Floating-point numbers do not use that representation. Moreover, representation of floating-point numbers is not defined by the language specification, In other words, floating-point numbers in C do not have "bits", which is why you won't be able to access any of their "bits" by any legal means of the language, and which is why you can't apply any bitwise operators to floating-point objects.

Having said that, I'd suspect that you might be interested in physical bits representing a floating-point object. You can reinterpret the memory occupied by the floating-point object (or any other object) as an array of unsigned char elements and print the bits of each of the unsigned char objects. That will give you the map of all physical bits representing the object.

However, this won't be exactly equivalent to what you have in your code above. Your code above prints the bits of value representation of an integral object (i.e it is the logical bits I described above), while the memory reinterpretation approach will give you the bits of the object representation (i.e. the physical bits). But then again, floating-point numbers in C don't have logical bits by definition.

Added later: I feel that understanding the difference between the concepts of physical and logical bits might not be an easy task for some readers. As another example that might help to promote the understanding, I'd like to note that there's absolutely nothing that would preclude a perfectly compliant C implementation on ternary hardware, i.e. hardware that does not have physical binary bits at all. In such implementation bitwise operations would still work perfectly fine, they would still access binary bits, i.e. elements of [now only imaginary] binary positional representation of each integral number. That would be the logical bits I'm talking about above.

AndreyT
if float doesn't have logical bits, how does it get stored in memory? Ex: 2.5
tsubasa
@tsubasa, this link might help you: http://stackoverflow.com/questions/56947/how-is-floating-point-stored-when-does-it-matter
hexium
Actually, I'm pretty sure that C99 specifies IEEE 754 floating-point, and that IEEE 754 in turn specifies the meaning of each bit in a floating-point number. On the other hand, C99 does not specify 2-complement representation for integers (although it is pervasive). So in fact, the representation of floats is better specified than the representation of ints.
Pascal Cuoq
Floats certainly have a bit representation. A certain number of bits represents the exponent and the rest represent the mantissa (with the first binary digit usually missing to save one bit). This is interesting enough to look at when learning how floating point data is handled in a computer.
Carl Smotricz
C99: http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336.pdf IEEE754: http://www.validlab.com/754R/nonabelian.com/754/comments/Q754.129.pdf
Pascal Cuoq
DigitalRoss
@tsubasa: What you see in memory is *physical* bits (bits of object-representation). What you see with bit-wise operators in C are *logical* bits (bits of value-representation). These two are not the same. The only type in C for which the two are the same is `unsigned char`. For all other types the number of *logical* bits is *less or equal* than the number of *physical* bits. The difference is taken up by so called *padding* bits. That's the way it is with integral types. With floating-point types... they have no *logical* bits at all, as I said above.
AndreyT
@Carl Smotricz: The representation you are referring to is a *physical* representation of a type. The code in the original post inspects *logical* representation of a type in C language. It is impossible to inspect *logical* representation of a floating-point type in C, simly because it doesn't have any.
AndreyT
@DigitalRoss: C language specification explicitly defines the notions of "value-forming bits" and "padding bits" in integral types. The code in the original post prints *value-forming* (logical) bits only. `unsigned char` memory reinterpretation will print *all* bits (physical bits). That's the difference I'm talking about. With floating-point type you are limited to the latter approach, because the former simply doesn't exist in C.
AndreyT
Hmm, I can't think of a single C type with padding bits in ia32 or x86-64. Do you have an example? I think as an educational activity, understanding the physical representation has value. Making a confusing distinction by being ultra-pedantic about terminology, not so much. But to play along I will point out that you should have said *value bits* not *value-forming* bits. Now, did that help?
DigitalRoss
This is simply wrong. IEEE-754 specifies the bit pattern. ANSI C standard section 3.14 defines that storage is consecutive bytes, section 3.4 defines byte as consisting of a contiguous series of bits and that bytes must be individually addressable. Nowhere in the standard is written "physical bit" or "logical bit." Do you have any reference citation?
Heath Hunnicutt
@Heath, he means C99 6.2.6.2 Integer Types, which goes on and on about *value bits* and *padding bits*. There were lots of not-quite-dead-yet wacky designs still found here and there like zombies back then, no doubt this was an attempt to allow someone's pet not-a-power-of-2 or not-byte-addressable and not-quite-dead-yet box to be technically standard-conforming. The only decent system I can think of in this category was the Cray series, but I can't imagine they cared if they were technically conforming or not.
DigitalRoss
@Heath, or, it could have been for something equally wacky like the now-forgotten-for-a-reason Intel 432 or a tagged architecture oddity like the Lisp Machine.
DigitalRoss
@Heath Hunnicutt: No, this is not wrong. This is absolutely correct. I don't understand why I have explain it again and agin after it has been explained already numerout times. Get a copy of the standard and read about representation of types, especially the parts about "object representation" and "value representation". Once you get at least the basic understanding of the concepts, it should became clear to you that "bits" as seen by C bitwise operatioins and actual hardware "bits" are two completely different concepts.
AndreyT
@Heath Hunnicutt: As for IEEE-754,, jow many times do I need to repeat that IEEE-754 is forumulated in terms of machine bits? Not even mentioning that we are talikng about C language here, which doesn't know what IEEE-754 is and doesn't give a flying squirrel about IEEE-754.
AndreyT
+2  A: 
static void printme(void *c, size_t n)
{
  unsigned char *t = c;
  if (c == NULL)
    return;
  while (n > 0) {
    --n;
    printf("%02x", t[n]);
  }
  printf("\n");
}

void fpp(float f, double d)
{
  printme(&f, sizeof f);
  printme(&d, sizeof d);
}
  • A note on float parameters

    Be sure you have the prototype for fpp() in scope when you call it or you will invoke an obscure K&R C vs ANSI C issue.

Update: binary output...

  while (n > 0) {
    int q;
    --n;
    for(q = 0x80; q; q >>= 1)
      printf("%x", !!(t[n] & q));
  }
DigitalRoss
printf("%02x", t[n]); shows only byte representation. Can you make it to show in bits? I want to see something like this:-118.625 written as 1000010111011010100000000000000 [According to http://en.wikipedia.org/wiki/IEEE_754-1985]
tsubasa
@tsubasa -- what Computer Science classes are you taking?
Heath Hunnicutt
A: 

print the integer part, then a '.', then the fractional part.

float f = ...
int int_part = floor(f)
int fraction_part = floor((f - int_part) * pow(2.0, 32))

You can then use your bit_return to print x and y. Bonus points for not printing leading and/or trailing zeros.

Keith Randall
+1  A: 

If you want to use your bit_return function on a float, you can just cheat:

float f = 42.69;

for .... 
   bit_return((int) f, loc)

The (int) cast will make the compiler believe you're working with an integer, so bit_return will work.

This is essentially what Pascal was suggesting.

EDIT:

I stand corrected by Pascal. I think this will conform with his latest comment:

bit_return (*((float *) &f), loc)

hope I got it right that time.

Another alternative (with fewer parentheses) would be to use a union to cheat on the data type.

Carl Smotricz
actually, `(int)` applied to a float is compiled in a rounding operation. You *really* need to take the address of f, cast it to a pointer to int, and dereference.
Pascal Cuoq
Or use a union.
Carl Norum
Pascal Cuoq, great comment. thanks. i finally figure out how to do it.#include <stdio.h>int bit_return(int a, int loc) // bit returned at location{ int buf = a if (buf == 0) return 0; else return 1;}int main(){ float a = -118.625; //11000010111011010100000000000000 |1 sign bit | 8 exponent bit | 23 fraction bits | int *b; b = int i; for (i = 31; i>=0; i--) { printf("%d",bit_return(*b,i)); } return 0;}
tsubasa
+2  A: 

The following code assumes floats and pointers are the same size, which is true on many systems:

float myfloat = 254940.4394f;
printf("0x%p", *(void**)(&myfloat));
cdiggins
+1  A: 

While from comments it seems that outputing the bits of the internal representation may be what was wanted, here is code to do what the question seemed to literally ask for, without the lossy conversion to int some have proposed:

Outputing a floating point number in binary:

#include <stdio.h>
#include <stdlib.h>

void output_binary_fp_number(double arg)
{
    double pow2;

    if ( arg < 0 ) { putchar('-'); arg = -arg; }
    if ( arg - arg != 0 ) {
        printf("Inf");
    }
    else {
        /* compare and subtract descending powers of two, printing a binary digit for each */
        /* first figure out where to start */
        for ( pow2 = 1; pow2 * 2 <= arg; pow2 *= 2 ) ;
        while ( arg != 0 || pow2 >= 1 ) {
            if ( pow2 == .5 ) putchar('.');
            if ( arg < pow2 ) putchar('0');
            else {
                putchar('1');
                arg -= pow2;
            }
            pow2 *= .5;
        }
    }

    putchar('\n');

    return;
}

void usage(char *progname) {
    fprintf(stderr, "Usage: %s real-number\n", progname);
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv) {
    double arg;
    char *endp;

    if ( argc != 2 ) usage(argv[0]);
    arg = strtod(argv[1], &endp);
    if ( endp == argv[1] || *endp ) usage(argv[0]);

    output_binary_fp_number(arg);

    return EXIT_SUCCESS;
}
ysth
Note that I'm intentionally avoiding things like frexp/isfinite in favour of simple arithmetic to make the code easier to learn from.
ysth
+1  A: 
Heath Hunnicutt
+1  A: 

Thanks to Pascal Cuoq for his comment. i finally figure out how to solve my own problem. yes, just assign address of the float number to a pointer to integer, then dereference it. Here is my code solution:

#include <stdio.h>

int bit_return(int a, int loc)  // bit
returned at location {
    int buf = a & 1<<loc;

    if (buf == 0) return 0;
    else return 1; }

int main() {
    float a = -118.625; //11000010111011010100000000000000  | 1 sign bit | 8 exponent bit | 23 fraction bits |
    int *b;
    b = &a;

    int i;
    for (i = 31; i>=0; i--)
    {
        printf("%d",bit_return(*b,i));
        }

    return 0; }
tsubasa