ItsASecret
ItsASecret

Reputation: 2649

C printf float rounding

I have to reimplement printf(3) with C without using any function that would do the conversion for me.

I'm nearly done I just need %a and it's also nearly done thanks to you guys : How %a conversion work in printf statement?

The man 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.

So my question is rounded how ?

I found: printf rounding behavior for doubles

And it explains that printf is using banker round or Round half to even but I have no idea how to implement it I have tried this:

a_double = round(a_double * pow(10, precision)) / pow(10, precision)

But on 1000000 tests starting from 0.000001 and adding 0.000001 each time it fails 405201 times, for example with 0.000011:

printf("%.6a", 0.000011) => 0x1.711948p-17
myprintf("%.6a", 0.000011) => 0x1.711947p-17

The rounding 'failed' and I didn't get the same value as the real printf.

I don't think that the algorithm that transforms the double to hexa notation is wrong because with a precision of 13 I have absolutely no errors.

So I just would like to know how I could do the same rounding that printf does on the double.

Upvotes: 0

Views: 1533

Answers (1)

juhist
juhist

Reputation: 4314

Ok, so I guess my previous algorithm didn't fully implement rounding so let's take a look how the result could be rounded. I'll use your example number as my example number too. First, we find that 0.000011/(2^(-17)) = 0.000011*(2^17) = 1.441792 so the power is -17. Then, we output "1.", subtract 1 from 1.441792 and multiply it by 16, giving 7.068672. We output 7, subtract 7 from it and multiply by 16, giving 1.09875199999999. We output 1, subtract 1 from it and multiply by 16, giving 1.58003199999985. We output 1, subtract 1 from it and multiply by 16, giving 9.28051199999754. Then output 9, subtract 9, multiply by 16, the result is 4.48819199996069. We output 4, subtract 4, multiply by 16, the result is 7.81107199937105.

Now, we're going to output the last character. Now we do the magic. Because 7.81107199937105 is closer to 8 than to 7, we output "8". This magic is done only for the last character. For the non-last characters, the integral part is always used and the fractional part is not used at all in determining which character to output. Then, after this, we output "p-17" because the power was -17.

Note that the usual rounding rules say that 7.5 which is equally close to both 7 and 8 is rounded to 8, not to 7 and 6.5 would be rounded to 7, not to 6. However, if you want to implement the round half to even and you encounter e.g. 6.5 then it is rounded down to 6 because 6 is even and 7 is not. I'm not sure if what you found about the banker round applies also to %a, the only thing you can do is to test implementing various rounding algorithms and see which gives the same results as the real %a of printf. Shouldn't be that hard because the different rounding algorithms just differ on how half is handled. The rest is rounded to the closest number.

By the way, I was wrong in your previous question (How %a conversion work in printf statement?) in saying that for 3.2 which has the non-rounded representation 1.999999....p+1 and the rounded representation 1.99999ap+1 the last "a" would occur due to limited floating point precision. Of course it occurs due to rounding and not due to limited floating point precision, as you probably have realized by now.

Upvotes: 1

Related Questions