3

I'm trying to implement printf and I want to know how printf rounds floating-point numbers because I cannot find a general rule

If for example input => printf("|%.f| |%.1f| |%.2f| |%.5f| |%.12f", 0.000099, 0.000099, 0.000099, 0.000099, 0.000099);

Here is the output => |0| |0.0| |0.00| |0.00010| |0.000099000000

I use the method from IEEE-754 so our floating-point number in memory is: 0.000098999999999999994037755413067714016506215557456016540527343750

My question is when and how should I round my floating-point number?

I am looking for a general rule that I must follow for all floating-point numbers.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Kchahid
  • 31
  • 1
  • 4
  • https://www.factmonster.com/math-science/mathematics/rounding-numbers-rules-examples-for-fractions-sums – Eugene Sh. Jun 11 '19 at 14:38
  • 2
    In what way is that rounding unexpected? It's exactly how I would round it. – klutt Jun 11 '19 at 14:42
  • 3
    Good luck with recoding `printf`, this is not something for beginners. Try easier things first – Jabberwocky Jun 11 '19 at 14:42
  • When should you round ? When he format string demands it. How should you round ? To the nearest value with the requested precision. – Sander De Dycker Jun 11 '19 at 14:49
  • if you see in the example we have as result 0.00010 and our float before round is 0.00009 we can say that 9 is rounded to 10 we write 0 and keep 1 to add it to 0 which gives us the 0.00010 but this rule is not general – Kchahid Jun 11 '19 at 15:09
  • if in this exemple => printf ("%. 7f", 0.000099); we have as a result 0.0000990 why in this case we did not round the 99 we can see that our float number is 0.000099 we can conclude that it is because we rounded only the digits >> precision of 6 but also it is not a general rule for all the numbers – Kchahid Jun 11 '19 at 15:14
  • When you wrote "our float before round is 0.00009", the value being rounded is not 0.00009, it is 0.000098999...something. – Ian Abbott Jun 11 '19 at 16:25
  • You need a better grasp of the concept of rounding. If you round $9.89 to the nearest dollar, you get $10, not $9. – Ian Abbott Jun 11 '19 at 16:39
  • @IanAbbott yes you're right but it's only to be a little clearer – Kchahid Jun 11 '19 at 16:39

3 Answers3

3

Not sure if this is exactly how printf does it, but this seems to work for your example:

Add 5/(10^(number of decimal points+1). Then truncate.

Your interpretation is flawed: Your C compiler is upcasting your constants to doubles. So it's not using 0.0000989999.... It's using the more accurate double equivalent.

Try this:

 printf("|%.f| |%.1f| |%.2f| |%.5f| |%.12f", (float)0.000099, (float)0.000099, (float)0.000099, (float)0.000099, (float)0.000099);

Output:

|0| |0.0| |0.00| |0.00010| |0.000098999997

yhyrcanus
  • 3,743
  • 2
  • 23
  • 28
  • thank you for your answer it's my fault it's me who did double instead of float but it's not a problem in this case because here I want to know how printf manages the rounding for float or double or long double – Kchahid Jun 11 '19 at 15:59
  • No upcasting involved: The constants *are* `double`-constants. – Deduplicator Jun 11 '19 at 16:00
3

The problem you are trying to address is actually very difficult to solve correctly.

Many existing printf implementations use conversion code dtoa.c written by David M. Gay almost 30 years ago.

You can learn more about this from this question, which is not an exact duplicate:

And these sites:

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • thank you for your answer I already recode printf and recode successfully float, double and long double thanks to the method ieee-754 but here I still have not found a general rule for the rounding that I should to implement in my code – Kchahid Jun 11 '19 at 16:04
2

The C standard provides the following in §7.21.6.1p13:

For e, E, f, F, g, and G conversions, if the number of significant decimal digits is at most DECIMAL_DIG, then the result should be correctly rounded. If the number of significant decimal digits is more than DECIMAL_DIG but the source value is exactly representable with DECIMAL_DIG digits, then the result should be an exact representation with trailing zeros. Otherwise, the source value is bounded by two adjacent decimal strings L<U, both having DECIMAL_DIG significant digits; the value of the resultant decimal string D should satisfy L ≤ D ≤ U, with the extra stipulation that the error should have a correct sign for the current rounding direction.

However, that paragraph is part of a subsection headed "Recommended Practice". (If Annex F is in effect, then the recommended practice is required. See F.5.)

"Correctly rounded" is defined by the current rounding direction. See fesetround.

ikegami
  • 367,544
  • 15
  • 269
  • 518
rici
  • 234,347
  • 28
  • 237
  • 341