i'm trying to find out what this program prints exactly.
#include <stdio.h>
int main() {
float bf = -62.140625;
int bi = *(int *)&bf;
int ci = bi (1<<23);
float cf = *(float *)&ci;
printf("%X\n",bi);
printf("%f\n",cf);
}
This prints out:
C2789000 -124.281250
But what happens line by line ? I do not understand .
Thanks in advance.
CodePudding user response:
It is a convoluted way of doubling an 32bit floating point number by adding one to its exponent. Moreover it is incorrect due to violation of strict aliasing rule by accesing object if type float
via type int
.
Exponent is located at bits number 23 to 30. Adding 1<<23
increment the exponent by one what works like multiplication of the original number by 2.
CodePudding user response:
If we rewrite this program to remove pointer punning
int main() {
float bf = -62.140625;
memcpy(&bi, &bf, sizeof(bi));
for(int i = 0; i < 32; i = 8)
printf("x ", ((unsigned)bi & (0xff << i)) >> i);
bi = (1<<23);
memcpy(&bf, &bi, sizeof(bi));;
printf("%f\n",bf);
}
Float numbers have the format:
-62.140625
has exponent == 0.
bi = (1<<23);
sets the exponent to 1
so the resulting float number will be -62.140625 * 2^1
and it is equal to -124.281250
. If you change that line to
bi = (1<<24);
it will set the exponent to 4
so the resulting float number will be -62.140625 * 2^2
and it is equal to -248.562500
.
CodePudding user response:
float bf = -62.140625;
This creates a float
object named bf
and initializes it to −62.140625.
int bi = *(int *)&bf;
&bf
takes the address of bf
, which produces a pointer to a float
. (int *)
says to convert this to a pointer to an int
. Then *
says to access the pointed-to memory, as if it were an int
.
The C standard does not define the behavior of this access, but many C implementations support it, sometimes requiring a command-line switch to enable support for it.
A float
value is normally encoded in some way. −62.140625 is not an integer, so it cannot be stored as a binary numeral that represents an integer. It is encoded. Reinterpreting the bytes memory as an int
using * (int *) &bf
is an attempt to get the bits into an int
so they can be manipulated directly, instead of through floating-point operations.
int ci = bi (1<<23);
The format most commonly used for the float
type is IEEE-754 binary32, also called “single precision.” In this format, bit 31 is a sign bit, bits 30-23 encode an exponent and/or some other information, and bits 22-0 encode most of a significand (or, in the case of a NaN, other information). (The significand is the fraction part of a floating-point representation. A floating-point format represents a number as ±F•be, where b is a fixed base, F is a number with a fixed precision in a certain range, and e is an exponent in a certain range. F is the significand.)
1<<23
is 1 shifted 23 bits, so it is 1 in the exponent field, bits 30-23.
If the exponent field contains 1 to 1021, then adding 1 to it increases the encoded exponent by 1. (The codes 0 and 1023 have special meaning in the exponent field. 1022 is a normal value, but adding 1 to it overflows the exponent in the special code 1023, so it will not increase the exponent in a normal way.)
Since the base b of a binary floating-point format is 2, increasing the exponent by 1 multiplies the number represented by 2. ±F•be becomes ±F•be 1.
float cf = *(float *)&ci;
This is the opposite of the previous reinterpretation: It says to reinterpet the bytes of the int
as a float
.
printf("%X\n",bi);
This says to print bi
using a hexadecimal format. This is technically wrong; the %X
format should be used with an unsigned int
, not an int
, but most C implementations let it pass.
printf("%f\n",cf);
This prints the new float
value.