gpt4 book ai didi

c - sprintf 格式化 : fixed number of characters, 小数位数可变

转载 作者:太空宇宙 更新时间:2023-11-04 01:46:17 26 4
gpt4 key购买 nike

我是looking对于类似 sprintf 的格式字符串,它将格式化具有可变 小数位数但固定 字符总数的 float (例如, 3)、为了提供最大限度的信息。例如:

0      ->  `.00` or `0.0` or `  0`
0.124 -> `.12`
0.357 -> `.36`
1.788 -> `1.8`
9.442 -> `9.4`
10.25 -> `10.` or ` 10`
75.86 -> `76.` or ` 76`
99.44 -> `99.` or ` 99`
100.0 -> `100`

(是的,我的数字都是 0 到 100 的 float )

如何实现?
这种定宽格式化是用sprintf格式化字符串语言实现的吗?

最佳答案

Is this kind of fixed-width formatting implemented in sprintf format string language?

没有。

How to achieve this?

为了满足 OP 的目标,代码可以分析 float f 的范围尝试使用最大信息进行打印。

"%.*f" 开头控制 . 之后的位数.

一个常见的陷阱发生在值略低于 10 的幂时,它会四舍五入并导致输出中出现另一个数字。

代码可以尝试针对 f < 0.995f 进行测试而不是 f < 1.0例如,鉴于浮点的二进制性质,这会导致失败的极端情况。

最好根据 1.0、10.0 等精确常数测试范围

下面最多尝试两次 sprintf() .第二次尝试使用那些讨厌的值,这些值刚好低于 10 的幂。

#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

void iago_sprint1(char *dest, int len, float f) {
if (f < 1.0) {
char buf[len + 2];
//printf("p1\n");
snprintf(buf, sizeof buf, "%.*f", len - 1, f);
if (buf[0] == '0') {
strcpy(dest, buf + 1);
return;
}
}
float limit = 10.0;
int prec = len - 2;

while (prec >= -1) {
if (f < limit) {
char buf[len + 2];
//printf("p2\n");
int cnt = snprintf(buf, sizeof buf, "%.*f", prec < 0 ? 0: prec, f);
if (cnt <= len) {
strcpy(dest, buf);
return;
}
}
prec--;
limit *= 10;
}
assert(0); // `f` was out of range
}

#define N 3
int main(void) {
float f[] = {0, 0.124f, 0.357f, 1.788f, 9.442f, 10.25f, 75.86f, 99.44f,
100.0f, 99.999f, 9.9999f, .99999f, .099999f, 1.04f, 1.06f};
char line[N + 1];

for (unsigned i = 0; i < sizeof f / sizeof *f; i++) {
//snprintf(line, sizeof line, "%#*g", N, f[i]);
//puts(line);

iago_sprint1(line, N, f[i]);
puts(line);
}
}

输出

.00
.12
.36
1.8
9.4
10
76
99
100
100
10
1.0
.10
1.0
1.1

预计算精度需要陷阱

代码是否应该尝试预先计算只有 1 所需的精度 sprintf()调用,计算需要像 sprintf() 一样推断精度确实 - 实际上代码正在重新执行 sprint()` 工作。

考虑 len==3float x = 9.95f; .作为二进制 float并不准确表示,它的值恰好高于或低于 9.951。如果在下方,则字符串应为 "9.9" ,如果在上面,"10." .如果代码有 double x = 9.95; (同样不能完全表示)输出可能不同。如果代码使用 float ,但是FLT_EVAL_MODE > 1 ,实际传递的值可能不是预期的 float 9.95 .

Precision-->  .1   .0 
9.94001... "9.9" "10."
9.94999... "9.9" "10." // Is this 9.95f
9.95001... "10.0" "10." // or this?
9.95999... "10.0" "10."

新的和改进的

我重新编写并简化了代码 - 在接受之后。

技巧 是用 "%*.f" 打印基于 f 的 10 次方计算的精度到一个缓冲区 比目标宽一个 - 假设由于四舍五入而没有进位到另一个数字。

10 的幂计算可以用一个小循环精确地完成。

当主角是 0 时, 它不需要像 "0.xx" 中那样--> ".xx" .

否则由于四舍五入而没有进位到另一个数字,字符串适合并且我们完成了。

否则为进位,则最后一个字符为 '.''0'小数点后,所以不需要。这发生在 f 时刚好低于 10 的幂,但打印版本四舍五入到 10 的幂。因此只复制长度为 1 的数字。

// `len` number of characters to print
// `len+1` is the size of `dest`
void iago_sprint3(char *dest, int len, float f) {
assert(len >= 1);
int prec = len - 1;
float power10 = 1.0;
while (f >= power10 && prec > 0) {
power10 *= 10;
prec--;
}

char buf[len + 2];
int cnt = snprintf(buf, sizeof buf, "%.*f", prec, f);
assert (cnt >= 0 && cnt <= len + 1);
if (buf[0] == '0') {
strcpy(dest, buf + 1);
return;
}
strncpy(dest, buf, (unsigned) len);
dest[len] = 0;
}

1 典型 float 9.95f正是

9.94999980926513671875    

典型 double 9.95正是

9.949999999999999289457264239899814128875732421875

关于c - sprintf 格式化 : fixed number of characters, 小数位数可变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53296362/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com