0

Why the printf below prints 123456.984 instead of 123456.988?

#include <stdio.h>
int main()
{
    printf("%.3f\n", 123456.987654);
    return 0;
}

Edit: i did a mistake, the right printf is:

printf("%.3f\n", 123456.987654f);
  • 3
    Perhaps `123456.984` is the closest (rounded) approximation of `123456.987654`? See e.g. [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) for details. – Some programmer dude Jun 07 '20 at 10:44
  • 1
    @Someprogrammerdude: No, `123456.987654` is a `double`, and the C standard requires `double` to have sufficient precision that “123456.988” would be produced. (`DBL_DIG` must be at least 10, so any 10-signficant-digit decimal number can be rounded to `double` and back again without change.) – Eric Postpischil Jun 07 '20 at 10:52
  • 1
    The code currently shown in the question outputs “123456.988” using Apple Clang 11.0.0 on macOS 10.14.6, and I expect it would in other common C implementations too. What compiler, compiler version, compiler flags/switches, and operating system are you using? To ensure this is the code that is producing the stated output, remove the executable, attempt to execute it to ensure it is gone, and rebuild it from scratch. – Eric Postpischil Jun 07 '20 at 10:58
  • @EricPostpischil In the question before in [this](https://stackoverflow.com/questions/62243319/printff-x-ok-printff-x-error-too-many-arguments-for-format/62243647#comment110083241_62243694) comment, OP stated s/he is using Code::Blocks with MingW-gcc under Windows. Probably that is her/his setting. – RobertS supports Monica Cellio Jun 07 '20 at 11:29
  • Federica, I suppose you put as constant a value that in your code is in a float variable. Unfortunately, using it as a constant makes the error not reproducible (constants are proessed as double, i.e. with a better precision). I therefore suggest that you either add a `f` suffix at the end of your constant, or you use a float variable as in your real code, so that everybody can understand what the problem is and most of the reader will be able to reproduce. – Christophe Jun 07 '20 at 11:48
  • 2
    In the absence of further information and edits from OP, this should be closed as not reproducible. The code shown in the question does not produce the output claimed in proper C implementations. The OP may have code that printed a `float` object or constant, and that would explain the output, but the question as currently presented seems incorrect. – Eric Postpischil Jun 07 '20 at 15:27
  • In the future *always* include a [mcve] to show us, one that we can copy and without modifications replicate the behavior you ask about. The code you show doesn't satisfy the replicable criteria. Also please take some time to read [the help pages](http://stackoverflow.com/help), take the SO [tour], read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Jun 07 '20 at 18:58

3 Answers3

1

The error is definitively related to floating point precision. It could be reproduced with float constants.

Executing this printf() with a float constant shows the behavior that you describe. If you pass a double constant, however, the result is as expected:

#include <stdio.h>
int main()
{
    printf("%.3f\n", 123456.987654f);  // float: insufficient precision .984
    printf("%.3f\n", 123456.987654);   // double: better precision .988
                    // unsuffixed floating point constants are double
    return 0;
}

Online demo

This is due to approximation in the internal representation of the floating point numbers, that use power of two and fractions of power of twos to represent the numbers. The closest representation corresponding to a number might not be close enough when converting back to decimal.

This strange rounding behavior is implementation dependent: The C standard does not specify which floating point representation has to be used.

Your compiler you use is certainly based on IEEE 754 standard. On this great web page, you can verify how a chosen floating point number is encoded using IEEE in single precision: for 123456.987654, almost the full range of bits is used and the closest number that can be represented in single precision is 123456.984375.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Re “power of two and fractions of power of twos”: I think you mean “powers of two, including powers with negative integer exponents.” – Eric Postpischil Jun 07 '20 at 11:07
  • 1
    @EricPostpischil indeed, there is no d, because by default it is d. Just used it because the compiler allows it and it puts the focus on the issue. And yes, for a scientist, power of two includes the full range positive and negative. But not everybody is that comfortable with the maths, so I prefer to be more explicit in the wording :-) – Christophe Jun 07 '20 at 11:11
  • @EricPostpischil I've removed the d and clarified in the comments to make it more explicit. And provided a nice reference to a site facilitating the analysis of single precision encoding of IEEE754. – Christophe Jun 07 '20 at 11:41
  • @RobertSsupportsMonicaCellio According to the C standard %f and %F work on a double argument: "A double argument representing a floating-point number is converted to decimal notation in the style [-]ddd.ddd, where the number of digits after the decimal-point character is equal to the precision specification." (N2176, section 7.21.6). - And double promotion is anyway performed when printf() is called. So the problem happens before the double promotion, i.e. in the float provided, because it is then the float value provided that is converted in double as it is. – Christophe Jun 07 '20 at 13:45
0

On my computer the programme below (compiled with gcc) prints out

123456.988
123456.988
123456.984

Was the code in your question actually what was showing the problem, or was there a float involved? If there was a float then the problem is that a float only has enough precision for about 7 figures. The closest float to your number, when printed to 3 decimal places is around 123456.984

#include    <stdio.h>
#include    <stdlib.h>

    int main( void)
    {
        printf("%.3f\n", 123456.987654);
        double  d = 123456.987654;
        printf("%.3f\n", d);
        float   f = 123456.987654;
        printf("%.3f\n", f);
        return EXIT_SUCCESS;
    }
dmuir
  • 4,211
  • 2
  • 14
  • 12
0

The issue you have seems to be implementation/compiler-specific (maybe even compilation- or execution-dependent). It is not reproducible for me.

With gcc, version 9.1 and clang, version 9.0 on Online Godbolt compiler and executor the output is 123456.988 for both.

Here is the Link.

  • I think OP put as constant something that was in reality in a float variable. If you add a f suffix to the constant you can reproduce it: https://godbolt.org/z/8nonV- Interestingly, with the float clang issues a hint about double promotion – Christophe Jun 07 '20 at 12:46
  • @Christophe Yes, something like that is possible and your answer is very good, but in general we shall take the code as it is. If OP really didn't showed the original issuing code, it is not good as we need a full MRE. - But I do not think that OP really messed things up in this way. I will continue writing in a comment to your question, [here](https://stackoverflow.com/questions/62244095/meaning-of-printf-3f-n-123456-987654/62244437?noredirect=1#comment110086799_62244352). – RobertS supports Monica Cellio Jun 07 '20 at 13:03