2

In C# I can do the following:

Console.Write("{0}, ", string.Format("{0:0.00###########}", someFloatValue));

to get at least 2 decimal places for some arbitrary float value up to some certain number of optional decimal places, in this case 13.

I was wondering if the same was possible with printf in C?

John Ernest
  • 785
  • 1
  • 8
  • 20
  • 1
    Does this answer your question -> https://stackoverflow.com/a/35121056/1737811 ? – mutantkeyboard May 29 '20 at 22:24
  • 1
    @mutantkeyboard The question isn't about specifying the precision in a variable. – Barmar May 29 '20 at 22:26
  • 1
    What does "optional decimal places" mean? Does it just mean that it will print 2-13 decimal places, but leave out trailing zeroes? – Barmar May 29 '20 at 22:29
  • 3
    If that's what you mean, there's nothing built-in to do it. See https://stackoverflow.com/questions/277772/avoid-trailing-zeroes-in-printf for workarounds. – Barmar May 29 '20 at 22:31
  • 2
    `for(char *end = buf + sprintf(buf, "%.*f", prec, val) - 1; end > buf && *end == '0'; --end) *end = '\0';` will print `val` to `buf` with `prec` decimals then discard the trailing 0s. There is no builtin floating-point format in C to (reliably) do that. – dxiv May 29 '20 at 22:58
  • @mutantkeyboard: I tried %.*f earlier and also %g but didn't get the result I wanted. %g is closer to what I want, but writes a number as -9.82567e-06 in C where in C# it would be like -0.00000982567, and the latter is preferred. – John Ernest May 30 '20 at 01:06
  • @Barmar: correct, if the number is 0.75 I wouldn't want to see the extra 0.75000000 padding, but I only want to see precision up to 13 decimals. In C# I can get a result like -0.00000982567 where in C using %g I would get -9.82567e-06, which is a tad harder to read, and wouldn't be a big deal if I weren't comparing >500 numbers at a time. – John Ernest May 30 '20 at 01:09

2 Answers2

1

Lop off trailing zero digits.

void formatDouble(char* buf, double val, int precMin, int precMax) {
  int length = sprintf(buf, "%.*f", precMax, val);
  if (isfinite(val) && length > 0) {
    for (int i = precMax; i > precMin; i--) {
      if (buf[length - 1] == '0') {
        buf[--length] = '\0';
      } else {
        break;
      }
    }
  }
}

void fooo(double d) {
  char buf[100];
  formatDouble(buf, d, 2,6);
  printf("<%s>\n", buf);
}

int main(void) {
  fooo(0.1234567890);
  fooo(0.123450);
  fooo(0.12300);
}

Output

<0.123457>
<0.12345>
<0.123>
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

Thanks for the links that gave some guidance. Here's what I came up with it lets you specify a minimum and maximum precision so it behaves more like the C# function, as well as rounds the last digit. Let me know if there are some optimizations or changes that should be made.

void formatDouble(char* buf, double val, int precMin, int precMax) {
    sprintf(buf, "%.*f", precMax, val);
    int startpos = 0;
    int length = strlen(buf);
    bool found = false;
    for (int z = 0; z < length; z++)
    {
        if (buf[z] == '.')
        {
            startpos = z;
            found = true;
            break;
        }
    }
    if (!found)
        return;
    int endIndex = startpos + precMin;
    found = false;
    for (int z = precMax + startpos; z >= startpos + precMin; z--)
    {
        if (buf[z] >= '1' && buf[z] <= '9')
        {
            endIndex = z;
            if (endIndex < startpos + precMax)
            {
                // Round Digit
                if (buf[z + 1] >= '5')
                    buf[z]++;
            }
            found = true;
            break;
        }
    }
    for (int z = endIndex + 1; z <= length; z++)
    {
        if (z <= startpos + precMax)
            buf[z] = 0;
    }
}
John Ernest
  • 785
  • 1
  • 8
  • 20
  • `if (buf[z + 1] >= '5')` is a [double rounding](https://en.wikipedia.org/wiki/Rounding#Double_rounding) as `val` is rounded to text and then then this code rounds again. – chux - Reinstate Monica Jun 01 '20 at 10:36