3

Originally I was using sprintf with floats always with 2 decimal places using the following code:

static void MyFunc(char* buffer, const float percentage)
{
    sprintf(buffer, "%.2f", percentage);
}

One of the percentage values passed was 0x419FFFFF 20 (debugger view), this printed 20.00 into buffer.

I would like instead to show 2 decimal places when not an integer, e.g.

94.74 displayed as 94.74
94.7  displayed as 94.70
0     displayed as 0
5     displayed as 5
100   displayed as 100

I am currently using the following code:

static void MyFunc(char* buffer, const float percentage)
{
    int fractional_part = ((percentage - (int)percentage) * 100);
    if (0 == fractional_part)
    {
        sprintf(buffer, "%d", (int)percentage);
    }
    else
    {
        sprintf(buffer, "%.2f", percentage);
    }
}

Now if 0x419FFFFF 20 (debugger view) is passed, fractional part is calculated as 99. I assume then the sum for fractional_part ends up being (19.99 - 19) * 100 = 99. Why then does the first example not print 19.99 into buffer?

What is the correct solution for my problem?

integra753
  • 271
  • 1
  • 5
  • 19
  • 2
    possible duplicate of [Only show decimal point if floating point component is not .00 sprintf/printf](http://stackoverflow.com/questions/838064/only-show-decimal-point-if-floating-point-component-is-not-00-sprintf-printf) – unwind Jan 24 '13 at 10:54
  • use "%g" instead of "%d" as indicated in the above link – MOHAMED Jan 24 '13 at 11:06
  • @MohamedKALLEL That's what the thread unwind referred to is suggesting. In my opinion it's really a duplicate. – junix Jan 24 '13 at 11:11
  • 1
    I require 2 decimal places, %.2g, sets the total number of digits to 2 not the number of decimal places. – integra753 Jan 24 '13 at 11:12

2 Answers2

6

Yours is a problem of approximation.

Suppose that the percentage is 19.999. Then fractional_part would be 99, and the floating point branch would be invoked.

But printing 19.999 with two decimals will round it to 20.00, and that is what is printed.

You could always use the floating point branch, in order to get consistent results, and then truncate at '.' if it comes out with '.00'. Otherwise, you risk your test and printf's internals to be at odds some time.

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
        float percentage = 19.999;
        char buffer[50];

        for (percentage = 19.990; percentage < 20.001; percentage += 0.001)
        {
                sprintf(buffer, "%.2f", percentage);
                char *p = strstr(buffer, ".00");
                if (p) *p = 0x0;
                printf("%.3f rendered as %.2f and becomes %s\n", percentage, percentage, buffer);
        }
        return 0;
}

19.990 rendered as 19.99 and becomes 19.99
19.991 rendered as 19.99 and becomes 19.99
19.992 rendered as 19.99 and becomes 19.99
19.993 rendered as 19.99 and becomes 19.99
19.994 rendered as 19.99 and becomes 19.99
19.995 rendered as 19.99 and becomes 19.99
19.996 rendered as 20.00 and becomes 20
19.997 rendered as 20.00 and becomes 20
19.998 rendered as 20.00 and becomes 20
19.999 rendered as 20.00 and becomes 20
20.000 rendered as 20.00 and becomes 20
20.001 rendered as 20.00 and becomes 20

If you don't agree with printf's rounding strategy, just use round() on (a copy of) percentage and force your own. Or you might also, e.g., sprintf() with three digits, and erase the third.

And in your specific case (note how my system (Linux x86_64) renders 0x419FFFFF):

#include <stdio.h>
#include <string.h>
#include <stdint.h>

int main(int argc, char **argv)
{
        float percentage = 3.1415;
        char buffer[50];

        ((uint32_t *)(&percentage))[0] = 0x419FFFFF;

        sprintf(buffer, "%.2f", percentage);
        char *p = strstr(buffer, ".00");
        if (p) *p = 0x0;
        printf("%.15f rendered as %.2f and becomes %s\n", percentage, percentage, buffer);
        return 0;
}


19.999998092651367 rendered as 20.00 and becomes 20
LSerni
  • 55,617
  • 10
  • 65
  • 107
0

Instead of calculating the fractional part yourself you can try to use ceilf(f) == f or floorf(f) == f which return true if f is an integer. Another alternative would be too use modf (float x, float *ipart) from std lib or fmod from math

Kris
  • 5,714
  • 2
  • 27
  • 47
  • Doing this my debugger gives percentage = 0x419FFFFF (20), ceilf(percentage) = 0x41A00000 (20), floorf(percentage) = 0x41980000 (19). Neither of these equal the original percentage. – integra753 Jan 24 '13 at 11:35
  • Neither of these equal the original percentage so my code executes the floating path and prints 20.00% which is exactly what I am trying to avoid. – integra753 Jan 24 '13 at 11:41