英文:
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 float
s 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
0
s 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 <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("");
}
}
Output
<%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>
答案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, "%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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论