如何使用C/C++格式说明符来实现与”g”相同的效果,但使用”1.”而不是”1.0″。

huangapple go评论73阅读模式
英文:

How to use C/C++ format specifiers to achieve the same as "g" but with "1." instead of "1.0"

问题

我想通过 printf 打印 float,如下所示:

  • (1) 右对齐到给定宽度的字符串(这里是 8
  • (2) 如果可能的话显示相关的小数位数,但在小数点后没有不必要的尾随 0
  • (3) 对于四舍五入的值也要做到这一点,即将 1.0 格式化为 "1."

使用 g 无法实现 (3),使用 f 无法实现 (2,3)。

文档中,似乎 # 可以完成 (3)

与 a、A、e、E、f、F、g 或 G 一起使用时,它会强制输出包含小数点,即使后面没有更多的数字。默认情况下,如果后面没有数字,就不会写入小数点。

因此,#g 可以实现 (3),但不幸的是它实际做得比文档中写的更多:它还通过移除相关的小数位数来破坏特性 (2)(见下文)。

这里是一些我尝试过的示例,最后一行显示了我想要的输出:

数字 1.0 -1.0 1.9 -999.1 1.000001
%8.2g 1 -1 1.9 -1e+03 1
%8.g 1 -1 2 -1e+03 1
%8g 1 -1 1.9 -999.1 1
%#8.2g 1.0 -1.0 1.9 -1.0e+03 1.0
%#8.g 1. -1. 2. -1.e+03 1.
%#8g 1.00000 -1.00000 1.90000 -999.100 1.00000
%8.2f 1.00 -1.00 1.90 -999.10 1.00
%8.f 1 -1 2 -999 1
%8f 1.000000 -1.000000 1.900000 -999.099976 1.000001
%#8.2f 1.00 -1.00 1.90 -999.10 1.00
%#8.f 1. -1. 2. -999. 1.
%#8f 1.000000 -1.000000 1.900000 -999.099976 1.000001
???? 1. -1. 1.9 -999.1 1.000001

有人能帮助我如何在最后一行实现所需的输出吗?

英文:

I'd like to print floats via printf as follows:

  • (1) right-align into a string of given width (here 8)
  • (2) show relevant decimal digits if possible, but no unneccesary trailing 0s after .
  • (3) also do this for rounded values, i.e. format 1.0 as "1."

With g I cannot achieve (3), with f I cannot achieve (2,3).

From the docs it seems that # would do the trick for (3)

> Used with a, A, e, E, f, F, g or G it forces the written output to contain a decimal point even if no more digits follow. By default, if no digits follow, no decimal point is written.

So #g can achieve (3), but it unfortunately does more than is written there: it also breaks the feature (2) by removing also relevant decimal digits (see below).

Here are some examples I tried, the last line shows what I am looking for:

number 1.0 -1.0 1.9 -999.1 1.000001
%8.2g 1 -1 1.9 -1e+03 1
%8.g 1 -1 2 -1e+03 1
%8g 1 -1 1.9 -999.1 1
%#8.2g 1.0 -1.0 1.9 -1.0e+03 1.0
%#8.g 1. -1. 2. -1.e+03 1.
%#8g 1.00000 -1.00000 1.90000 -999.100 1.00000
%8.2f 1.00 -1.00 1.90 -999.10 1.00
%8.f 1 -1 2 -999 1
%8f 1.000000 -1.000000 1.900000 -999.099976 1.000001
%#8.2f 1.00 -1.00 1.90 -999.10 1.00
%#8.f 1. -1. 2. -999. 1.
%#8f 1.000000 -1.000000 1.900000 -999.099976 1.000001
???? 1. -1. 1.9 -999.1 1.000001

Can somebody help how to achieve the wanted output in the last line?

答案1

得分: 2

建议:

修改目标

使用"%8g",因为这是标准的,并且最接近原作者的目标。

后处理输出

"#""%g"中修改了两个方面:始终打印一个'.',并且不会丢弃尾随的零。原作者似乎只想要第一个特性。

使用"%#*.*g", n, n-1, ...并在后处理字符串。

这很棘手,值得进行广泛测试。

不要

  • 在打印之前对float进行预处理。边缘情况会让你出错。

  • 在后处理中添加文本。边缘情况会让你出错。

  • "%e"不提供非指数形式。

  • "%f"可能会导致像-FLT_MAX这样的值输出很长。

示例代码。存在简化。

#include <stdio.h>
int main() {
  float val[] = {1.0, -1.0, 1.9f, -999.1f, 1.000001f};
  size_t v_n = sizeof val / sizeof val[0];
  puts("????        1.             -1.             1.9          -999.1        1.000001");
  fputs("           ", stdout);
  for (size_t v_i = 0; v_i < v_n; v_i++) {
    double v = val[v_i];
    char s[100];
    int n = 8;
    snprintf(s, sizeof s, "%#*.*g", n, n - 1, v);
    char *dot = strchr(s, '.');
    if (dot) {
      char *begin = dot + 1;
      begin = begin + strspn(begin, "0123456789");
      char *end = begin;
      while (begin[-1] == '0')
        begin--;
      if (begin < end) {
        //printf("xx: <%s> <%s>\n", begin, end);
        size_t len = strlen(end);
        memmove(begin, end, len + 1);
      }
    }
    printf(" %-13s", s);
  }
}

输出

????        1.             -1.             1.9          -999.1        1.000001
           1.           -1.          1.9          -999.1       1.000001
英文:

Suggestions:

Modify goal

Use "%8g" as that is standard and closest to OP's goal.

Post process output

"#" in "%g" modifies 2 things: a '.' is always printed and trailing zeroes are not discarded. OP seems to want just the first feature.

Use "%#*.*g", n, n-1, ... and post-process the string.

This is tricky and deserves extensive testing.

Do not

  • Pre-process the float before printing. Edge cases will catch you.

  • Add text in post processing. Edge cases will catch you.

  • "%e" does not provide non-exponential form.

  • "%f" can make for long output with values like -FLT_MAX.

Sample code. Simplifications exists.

#include <stdio.h>
int main() {
  float val[] = {1.0, -1.0, 1.9f, -999.1f, 1.000001f};
  size_t v_n = sizeof val / sizeof val[0];
  puts("????        1.             -1.             1.9          -999.1        1.000001");
  fputs("           ", stdout);
  for (size_t v_i = 0; v_i < v_n; v_i++) {
    double v = val[v_i];
    char s[100];
    int n = 8;
    snprintf(s, sizeof s, "%#*.*g", n, n - 1, v);
    char *dot = strchr(s, '.');
    if (dot) {
      char *begin = dot + 1;
      begin = begin + strspn(begin, "0123456789");
      char *end = begin;
      while (begin[-1] == '0')
        begin--;
      if (begin < end) {
        //printf("xx: <%s> <%s>\n", begin, end);
        size_t len = strlen(end);
        memmove(begin, end, len + 1);
      }
    }
    printf(" %-13s", s);
  }
}

Output

????        1.             -1.             1.9          -999.1        1.000001
           1.           -1.          1.9          -999.1       1.000001     

答案2

得分: 0

以下是代码的翻译部分:

#include <stdio.h>
int main() {
  float val[] = {1.0, -1.0, 1.9f, -999.1f, 1.000001f};
  size_t v_n = sizeof val / sizeof val[0];
  const char *format[] = {"%8.2g", "%8.g", "%8g", "%#8.2g", "%#8.g", "%#8g",
      "%8.2f", "%8.f", "%8f", "%#8.2f", "%#8.f", "%#8f"};
  size_t f_n = sizeof format / sizeof format[0];
  for (size_t f_i = 0; f_i < f_n; f_i++) {
    char f[100];
    snprintf(f, sizeof f, "<%s>", format[f_i]);
    printf("%-13s, ", f);
    for (size_t v_i = 0; v_i < v_n; v_i++) {
      double v = val[v_i];
      char s[100];
      snprintf(s, sizeof s, f, v);
      printf("%-13s", s);
    }
    puts("");
  }
}

输出:

<%8.2g>      , <       1>   <      -1>   <     1.9>   <  -1e+03>   <       1>   
<%8.g>       , <       1>   <      -1>   <       2>   <  -1e+03>   <       1>   
<%8g>        , <       1>   <      -1>   <     1.9>   <  -999.1>   <       1>   
<%#8.2g>     , <     1.0>   <    -1.0>   <     1.9>   <-1.0e+03>   <     1.0>   
<%#8.g>      , <      1.>   <     -1.>   <      2.>   < -1.e+03>   <      1.>   
<%#8g>       , < 1.00000>   <-1.00000>   < 1.90000>   <-999.100>   < 1.00000>   
<%8.2f>      , <    1.00>   <   -1.00>   <    1.90>   <-999.10>   <    1.00>   
<%8.f>       , <       1>   <      -1>   <       2>   <    -999>   <       1>   
<%8f>        , <1.000000>   <-1.000000>  <1.900000>   <-999.099976><1.000001>   
<%#8.2f>     , <    1.00>   <   -1.00>   <    1.90>   <-999.10>   <    1.00>   
<%#8.f>      , <      1.>   <     -1.>   <      2.>   <   -999.>   <      1.>   
<%#8f>       , <1.000000>   <-1.000000>  <1.900000>   <-999.099976><1.000001>
英文:

Some test for for anyone to use.
It may help this investigation.

#include &lt;stdio.h&gt;
int main() {
  float val[] = {1.0, -1.0, 1.9f, -999.1f, 1.000001f};
  size_t v_n = sizeof val / sizeof val[0];
  const char *format[] = {&quot;%8.2g&quot;, &quot;%8.g&quot;, &quot;%8g&quot;, &quot;%#8.2g&quot;, &quot;%#8.g&quot;, &quot;%#8g&quot;,
      &quot;%8.2f&quot;, &quot;%8.f&quot;, &quot;%8f&quot;, &quot;%#8.2f&quot;, &quot;%#8.f&quot;, &quot;%#8f&quot;, };
  size_t f_n = sizeof format / sizeof format[0];
  for (size_t f_i = 0; f_i &lt; f_n; f_i++) {
    char f[100];
    snprintf(f, sizeof f, &quot;&lt;%s&gt;&quot;, format[f_i]);
    printf(&quot;%-13s, &quot;, f);
    for (size_t v_i = 0; v_i &lt; v_n; v_i++) {
      double v = val[v_i];
      char s[100];
      snprintf(s, sizeof s, f, v);
      printf(&quot;%-13s&quot;, s);
    }
    puts(&quot;&quot;);
  }
}

Output

&lt;%8.2g&gt;      , &lt;       1&gt;   &lt;      -1&gt;   &lt;     1.9&gt;   &lt;  -1e+03&gt;   &lt;       1&gt;   
&lt;%8.g&gt;       , &lt;       1&gt;   &lt;      -1&gt;   &lt;       2&gt;   &lt;  -1e+03&gt;   &lt;       1&gt;   
&lt;%8g&gt;        , &lt;       1&gt;   &lt;      -1&gt;   &lt;     1.9&gt;   &lt;  -999.1&gt;   &lt;       1&gt;   
&lt;%#8.2g&gt;     , &lt;     1.0&gt;   &lt;    -1.0&gt;   &lt;     1.9&gt;   &lt;-1.0e+03&gt;   &lt;     1.0&gt;   
&lt;%#8.g&gt;      , &lt;      1.&gt;   &lt;     -1.&gt;   &lt;      2.&gt;   &lt; -1.e+03&gt;   &lt;      1.&gt;   
&lt;%#8g&gt;       , &lt; 1.00000&gt;   &lt;-1.00000&gt;   &lt; 1.90000&gt;   &lt;-999.100&gt;   &lt; 1.00000&gt;   
&lt;%8.2f&gt;      , &lt;    1.00&gt;   &lt;   -1.00&gt;   &lt;    1.90&gt;   &lt; -999.10&gt;   &lt;    1.00&gt;   
&lt;%8.f&gt;       , &lt;       1&gt;   &lt;      -1&gt;   &lt;       2&gt;   &lt;    -999&gt;   &lt;       1&gt;   
&lt;%8f&gt;        , &lt;1.000000&gt;   &lt;-1.000000&gt;  &lt;1.900000&gt;   &lt;-999.099976&gt;&lt;1.000001&gt;   
&lt;%#8.2f&gt;     , &lt;    1.00&gt;   &lt;   -1.00&gt;   &lt;    1.90&gt;   &lt; -999.10&gt;   &lt;    1.00&gt;   
&lt;%#8.f&gt;      , &lt;      1.&gt;   &lt;     -1.&gt;   &lt;      2.&gt;   &lt;   -999.&gt;   &lt;      1.&gt;   
&lt;%#8f&gt;       , &lt;1.000000&gt;   &lt;-1.000000&gt;  &lt;1.900000&gt;   &lt;-999.099976&gt;&lt;1.000001&gt;   

答案3

得分: 0

无法仅使用 printf 获得所需的输出,但您可以使用 snprintf 并进行如下调整输出:

int format_number(double v) {
    char buf[500];
    int len = snprintf(buf, sizeof buf, "%f", v);
    while (len > 0 && buf[len - 1] == '0')
        buf[--len] = '
int format_number(double v) {
    char buf[500];
    int len = snprintf(buf, sizeof buf, "%f", v);
    while (len > 0 && buf[len - 1] == '0')
        buf[--len] = '\0';
    return printf("%8s", buf);
}
'
;
return printf("%8s", buf); }
英文:

You cannot obtain the desired output with printf alone, but you can use snprintf and adjust the output this way:

int format_number(double v) {
    char buf[500];
    int len = snprintf(buf, sizeof buf, &quot;%f&quot;, v);
    while (len &gt; 0 &amp;&amp; buf[len - 1] = &#39;0&#39;)
        buf[--len] = &#39;
int format_number(double v) {
char buf[500];
int len = snprintf(buf, sizeof buf, &quot;%f&quot;, v);
while (len &gt; 0 &amp;&amp; buf[len - 1] = &#39;0&#39;)
buf[--len] = &#39;\0&#39;;
return printf(&quot;%8s&quot;, buf);
}
&#39;; return printf(&quot;%8s&quot;, buf); }

huangapple
  • 本文由 发表于 2023年2月27日 18:33:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/75579337.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定