0

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.

Community
  • 1
  • 1
ItsASecret
  • 2,589
  • 3
  • 19
  • 32
  • If you need to do this on a common binary floating point machine, there is no rounding needed for `"%a"` – chux - Reinstate Monica Mar 20 '15 at 00:23
  • I suspect this issue is with "starting from 0.000001 and adding 0.000001 each time" and not with `myprintf()`. What does "adding 0.000001" have anything to do with `myprintf()`? Remember that `0.000001` is not _exactly_ `0.000001` with binary floating point. Post more of your code showing how you are generating the test numbers and why you think you are getting incorrect results. – chux - Reinstate Monica Mar 20 '15 at 00:32
  • I start with `0.000001` and add `0.000001` each time and then I diff the result of the official printf and mine, mine is wrong because I don't know how `printf` does its rounding. Also I have absolutely no errors when the precision is >= `13` because no rounding occurs. – ItsASecret Mar 20 '15 at 10:08

1 Answers1

1

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.

Community
  • 1
  • 1
juhist
  • 4,210
  • 16
  • 33
  • I implemented it and it works for your example `0.000011` but not for `0.000005` I get `0x1.4f8b59p-18` instead of `0x1.4f8b58p-18` (if we don't specify a precision the result is `0x1.4f8b588p-18`) – ItsASecret Mar 20 '15 at 10:19
  • For the last digit the result is: `8.5555200017988682` which is rounded to `9` and that's why I get a `9`, dont know why `printf` leave it at `8` – ItsASecret Mar 20 '15 at 10:28
  • That being said it's still way better than what I had before, now only `60572` fails out of `1000000`. – ItsASecret Mar 20 '15 at 10:36
  • I wonder why you got 0x1.4f8b58p-18. On my system, printf("%.6a\n", 0.000005); prints 0x1.4f8b59p-18. You are not using Linux, I guess? I think the printf of your system is flawed. The only way forward would be to read the source code of your system's printf implementation which may not be available if it's not open-source. – juhist Mar 20 '15 at 11:39
  • I'm using OS X 10.10.2, very weird indeed thank you very much for your help :) – ItsASecret Mar 20 '15 at 12:01