Question

Hey i need to know how %f works , that is how

printf("%f",number);

extract a floating point number from a series of bits in number. Consider the code:

 main()
{
int i=1;
printf("\nd  %d\nf %f",i,i);
}

Output is :

d 1

f -0.000000

So ultimately it doesn't depend on variable 'i', but just depends on the usage of %d and %f(or whatever) i just need to know how %f extracts the float number corresponding to series of bits in 'i'

To all those who misunderstood my question i know that %f can't be used to an integer and would load garbage values if size of integer was smaller than float. As for my case the size of integer and float are 4 bytes.

Let me be clear if value of is 1 then the corresponding binary value of i will be this:

0000 0000 0000 0000 0000 0000 0000 0001 [32 bits]

How would %f extract -0.0000 as in this case from this series of bits.(How it knows where to put decimal point etc , i can't find it from IEEE 754)

[PLEASE DO CORRECT ME IF I AM WRONG IN MY EXPLANATION OR ASSUMPION]

Was it helpful?

Solution

It's undefined behavior to use "%f" to an int, so the answer to your question is: you don't need to know, and you shouldn't do it.

The output depends on the format specifier like "%f" instead of the type of the argument i is because variadic functions (like printf() or scanf()) have no way of knowing the type of variable argument part.

OTHER TIPS

As others have said, giving mismatched "%" specifier and arguments is undefined behavior, and, according to the C standard, anything can happen.

What does happen, in this case, on most modern computers, is this:

printf looks at the place in memory where the data should have been, interprets whatever data it finds there as a floating-point number, and prints that number. Since printf is a function that can take a variable number of arguments, all floats are converted to doubles before being sent to the function, so printf expects to find a double, which (on normal modern computers) is 64 bits. But you send an int, which is only 32 bits, so printf will look at the 32 bits from the int, and 32 more bits of garbage that just happened to be there. When you tried this, it seems that the combination was a bit pattern corresponding to the double floating-point value -0.0.

Well.

It's easy to see how an integer can be packed into bytes, but how do you represent decimals?

The simplest technique is fixed point: of the n bits, the first m are before the point and the rest after. This is not a very good representation, however. Bits are wasted on some numbers, and it has uniform precision, while in real life, most desired decimals are between 0 and 1.

Enter floating point. The IEEE 754 spec defines a way of interpreting bits that has, since then, been almost universally accepted. It has very high near-zero precision, is compact, expandable and allows for very large numbers as well.

The linked articles are a good read.

You can output a floating-point number (float x;) manually by treating the value as a "black box" and extracting the digits one-by-one.

First, check if x < 0. If so, output a minus-sign - and negate the number. Now we know that it is positive.

Next, output the integer portion. Assign the floating-point number to an integer variable, which will truncate it, ie. int integer = x;. Then determine how many digits there are using the base-10 logarithm log10(). Note, log10(0) is undefined, so you'll have to handle zero as a special case. Then iterate from 0 up to the number of digits, each time dividing by 10^digit_index to move the desired digit into the unit's position, and take the 10-residue (modulus).

for (i=digits; i>=0; i--)
    dig = (integer / pow(10,i)) % 10;

Then, output the decimal point ..

For the fractional part, subtract the integer from the original (absolute-value, remember) floating-point number. And output each digit in a similar way, but this time multiplying by 10^frac_digits. You won't be able to predict the number of significant fractional digits this way, so just use a fixed precision (constant number of fractional digits).

I have C code to fill a string with the representation of a floating-point number here, although I make no claims as to its readability.

IEEE formats store the number as a normalized binary fraction. It's more similar to scientific notation, like 3.57×102 instead of 357.0. So it is stored as an exponent-mantissa pair. Being "normalized" means there's actually an implicit additional 1 bit at the front of the mantissa that is not stored. Hopefully that's enough to help you understand a more detailed description of the format from elsewhere.

Remember, we're in binary, so there's no "decimal point". And with the exponent-mantissa notation, there isn't even a binary point in the format. It's implicitly represented in the exponent.


On the tangentially-related issue of passing floats to printf, remember that this is a variadic function. So it does not declare types of arguments that it receives, and all arguments passed undergo automatic conversions. So, float will automatically promote to double. So what you're doing is (substituting hex for brevity), passing 2 64-bit values:

double f,             double f
0xabcdefgh 0xijklmnop 0xabcdefgh 0xijklmnop

Then you tell printf to interpret this sequence of words as an int followed by a double. So the 32-bit int seen by printf is only the first half of the floating-point number, and then the floating-point number seem by printf has its words reversed. The fourth word is never used.

To get the integer representation, you'll need to use type-punning with a pointer.

printf("%d %f\n", *(int *)&f, f);

Which reads (from right-to-left): take the address of the float, treat it as a pointer-to-int, follow the pointer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top