6

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

I am able to do it for integers 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 replaced int a with float a.

Is there a solution for this?


Copy and reformat your comment below

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;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • What exactly do you want? Do you want to print "2.5" as "10.1" or do you want to print out the internal representation of the IEEE floating point number? – Heinzi Nov 08 '09 at 18:26
  • i want to represent 2.5 in binary. Ex in integer: 1 = 00000001. So, 2.5 = ? –  Nov 08 '09 at 18:28
  • Could you re-post your code? Some of it seems to have been cut off (no mention of loc) – HalfBrian Nov 08 '09 at 18:31
  • int bit_return(int a, int loc) // bit returned at location { int buf = a & 1< –  Nov 08 '09 at 18:33
  • Ok, for people who are not clear, i post my whole code here: #include #include int bit_return(int a, int loc) // bit returned at location { int buf = a & 1<=0; i--) { printf("%d",bit_return(a,i)); } return 0; } –  Nov 08 '09 at 18:35
  • from the code, i can write 32 digits of an int a=289642. now i want to write 32 digits of a float number (ex float a = 2.5). how would I do that? –  Nov 08 '09 at 18:37
  • tsubasa, that's hard on the eyes. You can go back and "edit" your post, and use the "code" formatting to make your code legible. Please. – Carl Smotricz Nov 08 '09 at 18:46
  • @tsubasa: you should know that your function could be written in one line: `return a & 1< – Evan Teran Nov 08 '09 at 19:01
  • not a single answer below addresses the main issue: he wants the **binary**, not **hexadecimal** representation – Matt Joiner Nov 08 '09 at 21:34
  • Can you tell us *why* you want to do this? It may shed some light on some areas where you seem to be making incorrect assumptions. – ysth Nov 08 '09 at 22:28
  • Pretty much a duplicate of SO 397692 http://stackoverflow.com/questions/397692/ – Jonathan Leffler Nov 09 '09 at 02:25

10 Answers10

6

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>

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

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

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

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

    return 0;
}
greatwolf
  • 20,287
  • 13
  • 71
  • 105
  • Why do `printf("%d",bit_return(*b,i));`? Wouldn't `printf("%d",bit_return(a,i));` be the same? Is there a reason for the extra pointer variable? – J...S Sep 29 '17 at 04:06
  • @J...S: `a` is a `float`. `*b` is intended to be the same bytes reinterpreted as an integer. However, this violates C’s aliasing rules, so the C standard does not guarantee it will work. – Eric Postpischil Jan 24 '18 at 00:43
  • 1
    A better method is `#include … unsigned b; memcpy(&b, &a, sizeof b);`, then pass `b` to `bit_return` instead of `*b`. This obeys C’s rules for examining the bytes of an object. `bit_return` should be changed to use `unsigned` instead of `int`, and change `1< – Eric Postpischil Jan 24 '18 at 00:46
  • But this is printing wrong. For a = 2.5, It's printing, `00000000000000000000000000000010`. – Appaji Chintimi Jun 01 '21 at 05:46
4

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
  • 79,187
  • 7
  • 161
  • 281
  • ... But don't be surprised if within your compiler's strict-aliasing optimization approach the resulting code will simply print some irrelevant garbage. – AnT stands with Russia Nov 08 '09 at 19:42
  • 2
    @AndreyT Yes, I just can't get the hang of strict aliasing. I would interpret the standard as meaning that `*(int*)(char*)&f` is compatible with strict aliasing, but in case compiler authors disagree, using a union would be safer. – Pascal Cuoq Nov 08 '09 at 20:26
  • AndreyT where would an alias, as opposed to a cast, come into play? Seems like nowhere. – Heath Hunnicutt Nov 08 '09 at 22:25
  • @Heath I believe that AndreyT was referring to the fact that gcc could proudly assume that `*(int*)&f` has not changed when only `f` has been modified, and "optimize" accordingly. Gcc developers like to mess with everyone's low-level code this way, as if anyone was choosing C for speed in 2009 (as opposed to using C for writing low-level code, which people still do in 2009). I formed my own opinion before reading http://lwn.net/Articles/316126/ but I have to say I pretty much agree. Enough with the stupid speed benchmarks. C programmers expect something else from their compiler. – Pascal Cuoq Nov 08 '09 at 22:36
  • 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. – AnT stands with Russia Nov 08 '09 at 23:41
  • @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 Nov 09 '09 at 00:39
  • 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. – AnT stands with Russia Nov 09 '09 at 15:23
  • @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 Nov 09 '09 at 18:46
  • 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 Nov 10 '09 at 05:18
  • 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 Nov 10 '09 at 05:26
4
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
  • 143,651
  • 25
  • 248
  • 329
  • 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] –  Nov 08 '09 at 19:24
  • @tsubasa -- what Computer Science classes are you taking? – Heath Hunnicutt Nov 08 '09 at 22:26
3

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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • if float doesn't have logical bits, how does it get stored in memory? Ex: 2.5 –  Nov 08 '09 at 18:40
  • @tsubasa, this link might help you: http://stackoverflow.com/questions/56947/how-is-floating-point-stored-when-does-it-matter – hexium Nov 08 '09 at 18:42
  • 1
    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 Nov 08 '09 at 18:42
  • 1
    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 Nov 08 '09 at 18:44
  • 1
    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 Nov 08 '09 at 18:45
  • Bits that are not bits! Cool! Can I get some of those? Actually, as usual, AndreyT is correct, although that's awfully pedantic to say about C, where bits really are bits. A good example of what AndreyT is talking about can easily be seen in javascript, however, where the native representation is fp and the value representation is conventional. `number & 1` in javascript *appears* to do the same thing as in C, but... – DigitalRoss Nov 08 '09 at 18:58
  • @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. – AnT stands with Russia Nov 08 '09 at 19:33
  • @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. – AnT stands with Russia Nov 08 '09 at 19:35
  • 1
    @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. – AnT stands with Russia Nov 08 '09 at 19:40
  • 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 Nov 08 '09 at 20:10
  • 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 Nov 08 '09 at 22:23
  • @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 Nov 08 '09 at 23:25
  • @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 Nov 08 '09 at 23:29
  • @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. – AnT stands with Russia Nov 08 '09 at 23:37
  • @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. – AnT stands with Russia Nov 08 '09 at 23:38
3

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
  • 17,602
  • 7
  • 105
  • 102
2

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
  • 96,171
  • 6
  • 121
  • 214
  • Note that I'm intentionally avoiding things like frexp/isfinite in favour of simple arithmetic to make the code easier to learn from. – ysth Nov 08 '09 at 22:34
2

I have included code which produces hexadecimal output that I think may help you understand floating-point numbers. Here is an example:

double: 00 00 A4 0F 0D 4B 72 42 (1257096936000.000000) (+0x1.24B0D0FA40000 x 2^40)

From my code example below, it should become obvious to you how to output the bits. Cast the double's address to unsigned char * and output the bits of sizeof(double) chars.

Since I want to output the exponent and significand (and sign bit) of a floating-point number, my example code digs into the bits of the IEEE-754 standard representation for 64-bit 'double precision' floating pointing point in radix 2. Therefore I do not use sizeof(double) other than to verify that the compiler and I agree that double means a 64-bit float.

If you would like to output the bits for a floating-point number of any type, do use sizeof(double) rather than 8.

void hexdump_ieee754_double_x86(double dbl)
{
    LONGLONG ll = 0;
    char * pch = (char *)&ll;
    int i;
    int exponent = 0;

    assert(8 == sizeof(dbl));

    // Extract the 11-bit exponent, and apply the 'bias' of 0x3FF.
    exponent = (((((char *)&(dbl))[7] & 0x7F) &lt;&lt; 4) + ((((char *)&(dbl))[6] & 0xF0) &gt;&gt; 4) & 0x7FF) - 0x3FF;

    // Copy the 52-bit significand to an integer we will later display
    for (i = 0; i &lt; 6; i ++)
        *pch++ = ((char *)&(dbl))[i];
    *pch++ = ((char *)&(dbl))[6] & 0xF;

    printf("double: %02X %02X %02X %02X %02X %02X %02X %02X (%f)",     
           ((unsigned char *)&(dbl))[0],
           ((unsigned char *)&(dbl))[1],
           ((unsigned char *)&(dbl))[2],
           ((unsigned char *)&(dbl))[3],
           ((unsigned char *)&(dbl))[4],
           ((unsigned char *)&(dbl))[5],
           ((unsigned char *)&(dbl))[6],
           ((unsigned char *)&(dbl))[7],
           dbl);

    printf( "\t(%c0x1.%05X%08X x 2^%d)\n", 
            (((char *)&(dbl))[6] & 0x80) ? '-' : '+',
            (DWORD)((ll & 0xFFFFFFFF00000000LL) &gt;&gt; 32),
            (DWORD)(ll & 0xFFFFFFFFLL),
            exponent);
}

Nota Bene: The significand is displayed as a hexadecimal fraction ("0x1.24B0D0FA40000") and the exponent is display as decimal ("40"). For me, this was an intuitive way to display the floating-point bits.

Conrad Parker
  • 854
  • 10
  • 23
Heath Hunnicutt
  • 18,667
  • 3
  • 39
  • 62
1

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
  • 66,391
  • 18
  • 125
  • 167
  • 3
    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 Nov 08 '09 at 18:58
  • Pascal Cuoq, great comment. thanks. i finally figure out how to do it. #include int bit_return(int a, int loc) // bit returned at location { int buf = a & 1<=0; i--) { printf("%d",bit_return(*b,i)); } return 0; } –  Nov 09 '09 at 07:05
0

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Keith Randall
  • 22,985
  • 2
  • 35
  • 54
0

I think the best way to address this question is to use an union

unsigned f2u(float f)
{
    union floatToUnsiged{
    float a;
    unsigned b;
    }test;

    test.a = f;
    return (test.b);
}
Nav
  • 1
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 02 '21 at 21:18