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

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

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这样的值输出很长。

示例代码。存在简化。

  1. #include <stdio.h>
  2. int main() {
  3. float val[] = {1.0, -1.0, 1.9f, -999.1f, 1.000001f};
  4. size_t v_n = sizeof val / sizeof val[0];
  5. puts("???? 1. -1. 1.9 -999.1 1.000001");
  6. fputs(" ", stdout);
  7. for (size_t v_i = 0; v_i < v_n; v_i++) {
  8. double v = val[v_i];
  9. char s[100];
  10. int n = 8;
  11. snprintf(s, sizeof s, "%#*.*g", n, n - 1, v);
  12. char *dot = strchr(s, '.');
  13. if (dot) {
  14. char *begin = dot + 1;
  15. begin = begin + strspn(begin, "0123456789");
  16. char *end = begin;
  17. while (begin[-1] == '0')
  18. begin--;
  19. if (begin < end) {
  20. //printf("xx: <%s> <%s>\n", begin, end);
  21. size_t len = strlen(end);
  22. memmove(begin, end, len + 1);
  23. }
  24. }
  25. printf(" %-13s", s);
  26. }
  27. }

输出

  1. ???? 1. -1. 1.9 -999.1 1.000001
  2. 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.

  1. #include <stdio.h>
  2. int main() {
  3. float val[] = {1.0, -1.0, 1.9f, -999.1f, 1.000001f};
  4. size_t v_n = sizeof val / sizeof val[0];
  5. puts("???? 1. -1. 1.9 -999.1 1.000001");
  6. fputs(" ", stdout);
  7. for (size_t v_i = 0; v_i < v_n; v_i++) {
  8. double v = val[v_i];
  9. char s[100];
  10. int n = 8;
  11. snprintf(s, sizeof s, "%#*.*g", n, n - 1, v);
  12. char *dot = strchr(s, '.');
  13. if (dot) {
  14. char *begin = dot + 1;
  15. begin = begin + strspn(begin, "0123456789");
  16. char *end = begin;
  17. while (begin[-1] == '0')
  18. begin--;
  19. if (begin < end) {
  20. //printf("xx: <%s> <%s>\n", begin, end);
  21. size_t len = strlen(end);
  22. memmove(begin, end, len + 1);
  23. }
  24. }
  25. printf(" %-13s", s);
  26. }
  27. }

Output

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

答案2

得分: 0

以下是代码的翻译部分:

  1. #include <stdio.h>
  2. int main() {
  3. float val[] = {1.0, -1.0, 1.9f, -999.1f, 1.000001f};
  4. size_t v_n = sizeof val / sizeof val[0];
  5. const char *format[] = {"%8.2g", "%8.g", "%8g", "%#8.2g", "%#8.g", "%#8g",
  6. "%8.2f", "%8.f", "%8f", "%#8.2f", "%#8.f", "%#8f"};
  7. size_t f_n = sizeof format / sizeof format[0];
  8. for (size_t f_i = 0; f_i < f_n; f_i++) {
  9. char f[100];
  10. snprintf(f, sizeof f, "<%s>", format[f_i]);
  11. printf("%-13s, ", f);
  12. for (size_t v_i = 0; v_i < v_n; v_i++) {
  13. double v = val[v_i];
  14. char s[100];
  15. snprintf(s, sizeof s, f, v);
  16. printf("%-13s", s);
  17. }
  18. puts("");
  19. }
  20. }

输出:

  1. <%8.2g> , < 1> < -1> < 1.9> < -1e+03> < 1>
  2. <%8.g> , < 1> < -1> < 2> < -1e+03> < 1>
  3. <%8g> , < 1> < -1> < 1.9> < -999.1> < 1>
  4. <%#8.2g> , < 1.0> < -1.0> < 1.9> <-1.0e+03> < 1.0>
  5. <%#8.g> , < 1.> < -1.> < 2.> < -1.e+03> < 1.>
  6. <%#8g> , < 1.00000> <-1.00000> < 1.90000> <-999.100> < 1.00000>
  7. <%8.2f> , < 1.00> < -1.00> < 1.90> <-999.10> < 1.00>
  8. <%8.f> , < 1> < -1> < 2> < -999> < 1>
  9. <%8f> , <1.000000> <-1.000000> <1.900000> <-999.099976><1.000001>
  10. <%#8.2f> , < 1.00> < -1.00> < 1.90> <-999.10> < 1.00>
  11. <%#8.f> , < 1.> < -1.> < 2.> < -999.> < 1.>
  12. <%#8f> , <1.000000> <-1.000000> <1.900000> <-999.099976><1.000001>
英文:

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

  1. #include &lt;stdio.h&gt;
  2. int main() {
  3. float val[] = {1.0, -1.0, 1.9f, -999.1f, 1.000001f};
  4. size_t v_n = sizeof val / sizeof val[0];
  5. 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;,
  6. &quot;%8.2f&quot;, &quot;%8.f&quot;, &quot;%8f&quot;, &quot;%#8.2f&quot;, &quot;%#8.f&quot;, &quot;%#8f&quot;, };
  7. size_t f_n = sizeof format / sizeof format[0];
  8. for (size_t f_i = 0; f_i &lt; f_n; f_i++) {
  9. char f[100];
  10. snprintf(f, sizeof f, &quot;&lt;%s&gt;&quot;, format[f_i]);
  11. printf(&quot;%-13s, &quot;, f);
  12. for (size_t v_i = 0; v_i &lt; v_n; v_i++) {
  13. double v = val[v_i];
  14. char s[100];
  15. snprintf(s, sizeof s, f, v);
  16. printf(&quot;%-13s&quot;, s);
  17. }
  18. puts(&quot;&quot;);
  19. }
  20. }

Output

  1. &lt;%8.2g&gt; , &lt; 1&gt; &lt; -1&gt; &lt; 1.9&gt; &lt; -1e+03&gt; &lt; 1&gt;
  2. &lt;%8.g&gt; , &lt; 1&gt; &lt; -1&gt; &lt; 2&gt; &lt; -1e+03&gt; &lt; 1&gt;
  3. &lt;%8g&gt; , &lt; 1&gt; &lt; -1&gt; &lt; 1.9&gt; &lt; -999.1&gt; &lt; 1&gt;
  4. &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;
  5. &lt;%#8.g&gt; , &lt; 1.&gt; &lt; -1.&gt; &lt; 2.&gt; &lt; -1.e+03&gt; &lt; 1.&gt;
  6. &lt;%#8g&gt; , &lt; 1.00000&gt; &lt;-1.00000&gt; &lt; 1.90000&gt; &lt;-999.100&gt; &lt; 1.00000&gt;
  7. &lt;%8.2f&gt; , &lt; 1.00&gt; &lt; -1.00&gt; &lt; 1.90&gt; &lt; -999.10&gt; &lt; 1.00&gt;
  8. &lt;%8.f&gt; , &lt; 1&gt; &lt; -1&gt; &lt; 2&gt; &lt; -999&gt; &lt; 1&gt;
  9. &lt;%8f&gt; , &lt;1.000000&gt; &lt;-1.000000&gt; &lt;1.900000&gt; &lt;-999.099976&gt;&lt;1.000001&gt;
  10. &lt;%#8.2f&gt; , &lt; 1.00&gt; &lt; -1.00&gt; &lt; 1.90&gt; &lt; -999.10&gt; &lt; 1.00&gt;
  11. &lt;%#8.f&gt; , &lt; 1.&gt; &lt; -1.&gt; &lt; 2.&gt; &lt; -999.&gt; &lt; 1.&gt;
  12. &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 并进行如下调整输出:

  1. int format_number(double v) {
  2. char buf[500];
  3. int len = snprintf(buf, sizeof buf, "%f", v);
  4. while (len > 0 && buf[len - 1] == '0')
  5. buf[--len] = '
    int format_number(double v) {
  6.     char buf[500];
  7.     int len = snprintf(buf, sizeof buf, "%f", v);
  8.     while (len > 0 && buf[len - 1] == '0')
  9.         buf[--len] = '\0';
  10.     return printf("%8s", buf);
  11. }
  12. ';
  13. return printf("%8s", buf);
  14. }
英文:

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

  1. int format_number(double v) {
  2. char buf[500];
  3. int len = snprintf(buf, sizeof buf, &quot;%f&quot;, v);
  4. while (len &gt; 0 &amp;&amp; buf[len - 1] = &#39;0&#39;)
  5. buf[--len] = &#39;
    int format_number(double v) {
  6. char buf[500];
  7. int len = snprintf(buf, sizeof buf, &quot;%f&quot;, v);
  8. while (len &gt; 0 &amp;&amp; buf[len - 1] = &#39;0&#39;)
  9. buf[--len] = &#39;\0&#39;;
  10. return printf(&quot;%8s&quot;, buf);
  11. }
  12. &#39;;
  13. return printf(&quot;%8s&quot;, buf);
  14. }

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:

确定