Maybe this doesn't belong on SO but I don't know where else.
I have to reimplement printf(3)
with C
without using any function that would do the conversion for me, I'm nearly done, but I'm stuck on %a
, I really don't understand what is happening here for example:
printf("%a\n", 3.0); //#=> 0x1.8p+1
printf("%a\n", 3.1); //#=> 0x1.8cccccccccccdp+1
printf("%a\n", 3.2); //#=> 0x1.999999999999ap+1
printf("%a\n", 3.3); //#=> 0x1.a666666666666p+1
printf("%a\n", 3.4); //#=> 0x1.b333333333333p+1
printf("%a\n", 3.5); //#=> 0x1.cp+1
printf("%a\n", 3.6); //#=> 0x1.ccccccccccccdp+1
Of course I read the man which says:
The double argument is rounded and converted to hexadecimal notation in the style[-]0xh.hhhp[+-]d, where the number of digits after the hexadecimal-point character is equal to the precision specification.
But this doesn't really help I don't understand the process that transforms 3.2
to 1.999999999999ap+1
I don't need any code but really more an explanation.
PS: If this isn't the place for this question could you point me to the right place?
EDIT: While @juhist
answer works for numbers >= 1.0
it doesn't explain how to get the result for numbers between 0.0
et 1.0
:
printf("%a\n", 0.01); //#=> 0x1.47ae147ae147bp-7
printf("%a\n", 0.1); //#=> 0x1.999999999999ap-4
printf("%a\n", 0.2); //#=> 0x1.999999999999ap-3
printf("%a\n", 0.3); //#=> 0x1.3333333333333p-2
printf("%a\n", 0.4); //#=> 0x1.999999999999ap-2
printf("%a\n", 0.5); //#=> 0x1p-1
printf("%a\n", 0.6); //#=> 0x1.3333333333333p-1
Also I would really like a precision on this The "a" near the end occurs due to limited floating point calculation precision
concerning the conversion: printf("%a\n", 3.2); //#=> 0x1.999999999999ap+1
EDIT2: Now the last mystery is to explain why in this case:
printf("%a\n", 0.1); //#=> 0x1.999999999999ap-4
The last 9
becomes and a
and in this case:
printf("%a\n", 0.3); //#=> 0x1.3333333333333p-2
The last 3
stays a 3
?
First you need to know what the representation 0xh.hhhh p±d,
mean? Let's understand it by taking an example of hexadecimal constant 0x1.99999ap+1
.
The digit 1
before the decimal point is a hex digit and the number
of hexadecimal digits after it (99999a
) is equal to the precision. 0x
is the hex introducer and the p
is exponent field. The exponent is a decimal number that indicates the power of 2
by which the significant part is multiplied.
So, when 0x1.99999ap+1
will be multiplied with 2
1
then it will be converted to 3.2
in decimal. Recall that how 1.55e+1
converted to 15.500000
in decimal. Similar thing is happening here.
Now you need to know the mathematics behind the conversion of 0x1.99999ap+1
to 3.2
. This will proceed as follows
1*160 + 9*16-1 + 9*16-2 + 9*16-3 + 9*16-4 + 9*16-5 + 10*16-1
Which is (in decimal) equal to
1.60000002384185791015625
You need only up to 1 precision. So, take 1.6
and multiply it with 2
1
. Which will give 3.2
.
To go to the reverse of the above process you need to find power of 2
by which the floating point number will be divided to obtain the digit 1
before decimal point. After that use successive multiplications to change the fractional part to hexadecimal fraction. Proceed as follows:
- 3.2/21 = 1.6
- Take integral part from 1.6 to obtain the hex-digit
1
before decimal point.- Multiply
.6
by16
. The the integral part obtained will becomes a numeral in the hexadecimal fraction. Repeat this step with the obtained fractional part to the desired precision (6 is default).
- .6 * 16 = 9.6 ---> Integral part = 9 Fractional part = .6
- .6 * 16 = 9.6 ---> Integral part = 9 Fractional part = .6
- .6 * 16 = 9.6 ---> Integral part = 9 Fractional part = .6
- .6 * 16 = 9.6 ---> Integral part = 9 Fractional part = .6
- .6 * 16 = 9.6 ---> Integral part = 9 Fractional part = .6
- .6 * 16 = 9.6 ---> Integral part = 9 Fractional part = .6
So the hexadecimal fraction will become .999999
Now combine hex indicator 0x
, hex-digit before decimal point and the hexadecimal fraction obtained along with exponent field to get the result.
3.210 = 0x1.999999p+116
Similarly you can obtain hexadecimal floating point number for numbers less than 1.0
., for example 0.01
. In this case to obtain the hex-digit 1
before decimal point you need to divide it with a number which is power of 2
. Since 128
(25) after multiplying with 0.01
will give the number whose integral part becomes 1
, 128*.01 = 1.28
. It means you need to multiply 0.01
by 1/2-5 or you can say you need to divide it by 2-5 to get 1.28
. Now apply the steps 2 and 3 stated above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With