I know double is stocked in 64 bits
Not necessarily. A "IEEE 754 double-precision binary floating-point" number is stocked in 64-bits. A "double" may be anything, it may not and it may follow IEEE 745 standard. You should check __STDC_IEC_559__ macro before assuming it is C11 Annex F.
If you want to manipulate floating point numbers, you should use frexp and other such functions specifically meant to abstractly manipulate the representation of floating point numbers, without any *(super unsafe casts*):
double d = DBL_MIN / 2;
int exponent;
double fraction = frexp(d, &exponent);
if (fraction == 0 && exponent == 0) abort(); /*handle error*/
printf("%g = %d * 2^%d * %f\n", d, d<0?-1:1, exponent, fraction);
how to resolve this part?
The 1.fraction represents a fractional number like 1.01010111.. in base-2. The digits after comma are just the bits in the fraction part of the floating point number, in order. The following program (with many bugs in it) is meant to output the floating point value in the representation in the form sign * 2^(exp) * [0/1].fraction(2), where fraction is in base-2:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <limits.h>
#include <float.h>
#if !__STDC_IEC_559__
#error
#endif
int main() {
double d = DBL_MIN / 2;
typedef union {
unsigned long long sign : 1;
unsigned long long exp : 11;
unsigned long long fract : 52;
} double64u;
double64u di;
static_assert(sizeof(double) == sizeof(double64u), "");
memcpy(&di, &d, sizeof(double));
// extract **binary** digits from value into buffer
char buffer[53] = {0};
char *p = buffer + 52;
unsigned long long tmp = di.fract;
for (int i = 0; i < 52; ++i) {
*(--p) = (tmp & 0x1) + '0';
tmp >>= 1;
}
char sign = di.sign < 0 ? -1 : 1;
bool normal = di.exp != 0;
printf("%g = \n", d);
if (normal) {
printf("%d * 2^(%d - 1023) * 1.%s(2)\n",
sign, di.exp, buffer);
} else {
printf("%d * 2^(1 - 1023) * 0.%s(2)\n",
sign, buffer);
}
}
On my x86-64 this program outputs:
1.11254e-308
1 * 2^(1 - 1023) * 0.1000000000000000000000000000000000000000000000000000(2)
You can then take the 0.10.. which is a base 2 number (so I added the (2) on the end) to some "binary to decimal converter", like rapidtables, and 0.1 in base-2 is 0.5 in base-10 (well, this example is simple anyway). So the number is:
1 * 2^(1 - 1023) * 0.5
which then you can use some unlimited calculator like bc and input the number to calculate the actual result:
$ bc
scale=400
1 * 2^(1 - 1023) * 0.5
.0000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000000000000000000000000000000000000\
00000000000000000000000000000000000011125369292536006915451163586662\
0203210960799023116591527666370844360221740695909792714157950
which is the same number as 1.11254e-308.
Printing floating point numbers yourself is a very hard job to do. I can recommend https://www.ryanjuckett.com/printing-floating-point-numbers/ and papers that introduced Grisu3 and Ryu and Errol1 algorithms. For inspiration, read code from existing implementations: newlib vfprintf.c cvt(), musl vfprintf.c fmt_fp(), glibc printf_fp_ stuff.