开发者

How to print out each bit of a floating point number?

开发者 https://www.devze.com 2022-12-10 16:23 出处:网络
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:

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;
}


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;
}


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


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));
  }


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));


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.


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;
}


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.


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.


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.


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);
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号