6

There are a few questions related to this, but I haven't seen one that correctly answers this question. I want to print a floating-point number, but I want the number of decimal places to be adaptive. As an example:

0      -> 0
1234   -> 1234
0.1234 -> 0.1234
0.3    -> 0.3

Annoyingly, the %f specifier will only print to a fixed precision, so it will add trailing zeros to all numbers that don't reach that precision. Some have suggested the %g specifier, which works for a set of numbers, but it will switch to scientific notation for some numbers, like this:

printf("%g", 1000000.0); // prints 1e+06

How can I print floating-point numbers without the unnecessary zeros, while still maintaining printf's standard accuracy for numbers that actually have fractional components?

Alexis King
  • 43,109
  • 15
  • 131
  • 205
  • @AlexeyFrunze I don't think that's a duplicate, since it's considerably more involved than I'm looking for. I'm fine with rounding to a few digits, I just don't want any trailing zeros. – Alexis King Mar 16 '13 at 20:45
  • But that's what you've asked for, quote: `How can I print floating-point numbers without the unnecessary zeros, but also without rounding or truncating the value?` – Alexey Frunze Mar 16 '13 at 20:57
  • @AlexeyFrunze Ah, so I did, my bad. That was more in reference to "answers" on similar questions that would advise using `%.2f` or similar. I've edited it to remove that language. – Alexis King Mar 16 '13 at 22:01
  • But now the language is vague. Define `fairly good accuracy for numbers that need it`. – Alexey Frunze Mar 16 '13 at 22:06
  • @AlexeyFrunze I mean printf's standard accuracy (I believe 6 decimal places) for numbers that actually have decimal components. Again, I don't want it to be super accurate, I'm fine with printf's accuracy, I just hate all the trailing zeros. – Alexis King Mar 16 '13 at 22:12
  • Sorry, but that's still vague. You should've been explained by now that in a binary floating-point number there are no independent decimal digits/components in the fractional part. So, again, what's the question, precisely? – Alexey Frunze Mar 16 '13 at 23:54
  • @AlexeyFrunze We can argue over technicalities, but I don't think this question really warrants it. I understand how floating point works, but it doesn't matter. I'm happy round the numbers, but this question doesn't require a complex understanding of IEEE 754 representation. All I want is the regular printf display of a floating-point number without any trailing zeros, if any. I think that any misunderstanding over that is unwarranted and pointless. I have solved my problem, thanks. – Alexis King Mar 17 '13 at 00:00
  • Oh hey, @JasonC, it’s been a while. How the heck did you find this question, then go out of your way to mark it as a dupe after *three years*? Anyway, it’s appreciated. ;) – Alexis King May 11 '16 at 07:14
  • @AlexisKing I stopped playing Factorio for a second to look for how to print floats without trailing zeroes (I'm rusty), and the top zillion Google results were duplicate SO questions. – Jason C May 11 '16 at 07:15
  • @JasonC Hah. Well, I suppose the answer is not very satisfying, is it? Anyway, remember to visit the Tavern sometime—you’re not even pingable anymore and the place is deathly dull these days. :P (Also, these comments will self-destruct.) – Alexis King May 11 '16 at 07:18
  • @AlexisKing Yes. The answers bummed me out. – Jason C May 11 '16 at 07:19

5 Answers5

12

Use snprintf to print to a temporary buffer then remove the trailing '0' characters manually. There is no other way that's both correct and reasonably easy to implement.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
5

Try:

printf("%.20g\n", 1000000.0); // = 1000000

This will switch to scientific notation after 20 significant digits (default is after 6 digits for "%g"):

printf("%.20g\n", 1e+19); // = 10000000000000000000
printf("%.20g\n", 1e+20); // = 1e+20

But be careful with double precision:

printf("%.20g\n", 0.12345);   // = 0.12345000000000000417
printf("%.15g\n", 0.12345);   // = 0.12345
flyingOwl
  • 244
  • 1
  • 5
5

The problem is that using IEEE standard 754 representation, floating point values (with a fractional part) can never have "trailing zeros".

Trailing zeros mean that the fractional value can be written as x/10^n for some integers x, n. But the only fractions that can be represented by this standard have the form x/2^n for some integers x, n.

So what you write as 0.1234 is represented using the bytes 0x3D 0xFC 0xB9 0x24. This is:

Sign = 0
Exponent = 01111011 (which means -4)
Significand: 1.11111001011100100100100

The significand means: 1 + 1/2 + 1/4 + 1/8 + 1/16 + 0/32 + 0/64 + 1/128 + 0/256 + 1/512 + 1/1024 + 1/2048 + 0/4096 + 0/8192 + ...

If you perform this calculation, you get 1.974400043487548828125.

So the number is + 1.974400043487548828125 * 2^(-4) = 0.1234000027179718

(I've calculated this using a computer, of course, so it could be off for the same reason...)

As you can see, the computer does not want to decide for you that you want to chop this number after 4 digits (only) and not after 9 digits (0.123400002). The point is that the computer doesn't see this number as 0.1234 with an infinite number of trailing zeros.

So I don't think there's a better way than R.'s.

Omri Barel
  • 9,182
  • 3
  • 29
  • 22
  • 1
    Understood. So perhaps I want to round to say, 8 decimal places, which would be 0.12340000. I still want to be able to print this rounded value without the extra zeros. – Alexis King Mar 16 '13 at 19:47
0

I wrote a small function to do this myself using the method already mentioned, here it is with a tester. I can't guarantee it's bug free but I think it's fine.

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

char *convert(char *s, double x);

int main()
{
    char str[3][20];
    printf("%s\n%s\n%s\n", convert(str[0], 0),
          convert (str[1], 2823.28920000),
          convert (str[2], 4.000342300));

}

char *convert(char *s, double x)
{
    char *buf = malloc(100);
    char *p;
    int ch;

    sprintf(buf, "%.10f", x);
    p = buf + strlen(buf) - 1;
    while (*p == '0' && *p-- != '.');
    *(p+1) = '\0';
    if (*p == '.') *p = '\0';
    strcpy(s, buf);
    free (buf);
    return s;
}

Output:

0
2823.2892
4.0003423
teppic
  • 8,039
  • 2
  • 24
  • 37
-1

You can print like this:

printf("%.0f", 1000000.0);

For a more detailed answer, look here.

Garrett Hyde
  • 5,409
  • 8
  • 49
  • 55
nabroyan
  • 3,225
  • 6
  • 37
  • 56
  • 1
    `printf("%.0f", 0.1234);` prints "0". I don't think the OP wanted that. – Omri Barel Mar 16 '13 at 19:15
  • "How can I print floating-point numbers without the unnecessary zeros, but also without rounding or truncating the value?" is the answer That prints 0, because is doesn't print after dot (fraction part) – nabroyan Mar 16 '13 at 19:16
  • This will truncate the number, won't it? – Metabble Mar 16 '13 at 19:17
  • @Metabble if you print a variable then that format will not change your variable. It is just a format for printing numbers – nabroyan Mar 16 '13 at 19:21
  • 2
    @nabroyan The question specifically says that 0.1234 should be printed as 0.1234. That's not what your answer is doing. – Omri Barel Mar 16 '13 at 19:21
  • @OmriBarel that's right, my answer is for cases that you know precision of that number – nabroyan Mar 16 '13 at 19:24