sprintf(buf, "%.20g", x) // buf 应该有多大?

发布于 2024-08-15 15:31:50 字数 465 浏览 5 评论 0 原文

我将双精度值转换为字符串,如下所示:

std::string conv(double x) {
    char buf[30];
    sprintf(buf, "%.20g", x);
    return buf;
}

我已将缓冲区大小硬编码为 30,但不确定这对于所有情况是否足够大。

  • 如何找出我需要的最大缓冲区大小?
  • 从 32 位切换到 64 时,精度是否会变得更高(因此缓冲区需要增加)?

PS:出于性能原因,我无法使用 ostringstreamboost::lexical_cast (请参阅这个

I am converting double values to string like this:

std::string conv(double x) {
    char buf[30];
    sprintf(buf, "%.20g", x);
    return buf;
}

I have hardcoded the buffer size to 30, but am not sure if this is large enough for all cases.

  • How can I find out the maximum buffer size I need?
  • Does the precision get higher (and therefore buffer needs to increase) when switching from 32bit to 64?

PS: I cannot use ostringstream or boost::lexical_cast for performance reason (see this)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

滥情空心 2024-08-22 15:31:50

我已将缓冲区大小硬编码为 30,但不确定这是否对于所有情况都足够大。

这是。 %.20g 指定尾数中的 20 位数字。小数点加 1。 1 表示(可能的)符号,5 表示“e+308”或“e-308”,最坏情况下的指数。 1 表示终止 null。

20 + 1 + 1 + 5 + 1 = 28。

从 32 位切换到 64 时,精度是否会提高(因此缓冲区需要增加)?

不会。

两种架构中的 double 大小相同。如果将变量声明为 long double,则指数“e+4092”中可能会多出 1 个数字,该数字仍然适合 30 个字符的缓冲区。但仅适用于 X86,且仅适用于较旧的处理器。

long double 是一种过时的 80 位浮点值形式,是 486 FPU 的本机格式。该 FPU 架构扩展性不佳,因此被放弃,取而代之的是 SSE 风格指令,其中最大可能的浮点值是 64 位双精度。

从长远来看,只要您将打印输出中的尾数限制为 20 位,30 个字符的缓冲区就始终足够。

I have hardcoded the buffer size to 30, but am not sure if this is large enough for all cases.

It is. %.20g specifies 20 digits in the mantissa. add 1 for decimal point. 1 for (possible) sign, 5 for "e+308" or "e-308", the worse case exponent. and 1 for terminating null.

20 + 1 + 1 + 5 + 1 = 28.

Does the precision get higher (and therefore buffer needs to increase) when switching from 32bit to 64?

No.

A double is the same size in both architectures. If you declare your variables as long double, then you possibly have 1 more digit in the exponent "e+4092", which still fits in a 30 character buffer. But only on X86, and only on older processors.

The long double is an obsolete 80 bit form of floating point value that was the native format of the 486 FPU. That FPU architecture didn't scale well and as since been discarded in favor of SSE style instructions where the largest possible floating point value is a 64 bit double.

Which is a long way of saying a buffer of 30 characters will always be sufficient as long as you keep limiting the mantissa in your printout to 20 digits.

微暖i 2024-08-22 15:31:50

printf("%.20g", 1.79769e+308);1.7976900000000000632e+308,27 个字节,包括结尾的 \0。为了确定起见,我会选择 64 或 128。

(因为它位于堆栈上并在您也可以使用大缓冲区(甚至 2048 字节)后立即释放,而不会遇到非嵌入式应用程序的问题)

另外,您确定程序的瓶颈是 lexical_cast 吗..?做你正在做的事情对我来说似乎很愚蠢

printf("%.20g", 1.79769e+308); is 1.7976900000000000632e+308, 27 bytes including the trailing \0. I would choose 64 or 128 just to be sure.

(Since it's on the stack and released right after you could also go with big buffers, even 2048 bytes, without running into problems for non embedded applications)

Also, are you sure the bottleneck of your program is lexical_cast..? Doing what you're doing seems very silly to me

幸福%小乖 2024-08-22 15:31:50

我似乎记得,如果您使用 NULL 目的地调用 sprintf ,它不会执行任何操作。但是,它确实返回它“写入”的字符数。如果我是对的(而且我似乎找不到来源),那么您可以这样做:

// find the length of the string
int len = sprintf(NULL, fmt, var1, var2,...);
// allocate the necessary memory.
char *output = malloc(sizeof(char) * (len + 1)); // yes I know that sizeof(char) is defined as 1 but this seems nicer.
// now sprintf it after checking for errors
sprintf(output, fmt, var1, var2,...);

另一个选择是使用 snprintf 它允许您限制输出的长度:

#define MAX 20 /* or whatever length you want */
char output[MAX];
snprintf(output, MAX, fmt, var1, var2,...);

snprintf 将缓冲区的大小作为参数,并且不允许输出字符串超过该大小。

I seem to remember that if you call sprintf with a NULL destination, it doesn't do anything. It does, however, return the number of chars that it "wrote". If I'm right (and I can't seem to find the source for that) then you can do:

// find the length of the string
int len = sprintf(NULL, fmt, var1, var2,...);
// allocate the necessary memory.
char *output = malloc(sizeof(char) * (len + 1)); // yes I know that sizeof(char) is defined as 1 but this seems nicer.
// now sprintf it after checking for errors
sprintf(output, fmt, var1, var2,...);

Another option is to use snprintf which allows you to limit the length of the output:

#define MAX 20 /* or whatever length you want */
char output[MAX];
snprintf(output, MAX, fmt, var1, var2,...);

snprintf takes the size of the buffer as an argument, and doesn't allow the output string to exceed that size.

深者入戏 2024-08-22 15:31:50

这是一个程序,用于打印任何系统中 double 可以采用的最大和最小值所需的位数:

#include <float.h>
#include <stdio.h>

int main(void)
{
    double m = DBL_MAX;
    double n = DBL_MIN;
    int i;
    i = printf("%.20g\n", m);
    printf("%d\n", i);
    i = printf("%.20g\n", n);
    printf("%d\n", i);
    return 0;
}

对我来说,它打印:

1.7976931348623157081e+308
27
2.2250738585072013831e-308
27

由于 27 包含换行符,但不包含终止 0 对于字符串,我想说在这个系统上,27 就足够了。对于 long double,在我的系统上,LDBL_MAXLDBL_MIN 的答案似乎分别是 27 和 28。

手册页(在我的 sprintf 上)这样描述了 %g

双参数被转换为
风格 f 或 e(或 F 或 E 代表 G
转换)。精度指定了数量
重要的
数字。如果精度缺失,则给出6位;如果
精度为零,则视为 1。如果精度为 0,则使用样式 e
转换后的指数小于 -4 或更大
比或
等于精度。尾随零被从
结果的小数部分;只出现小数点
如果它
后面至少跟一位数字。

C 标准中也有类似的措辞。

因此,我认为如果您使用上述程序的输出作为数组大小,您将是安全的。

Here's a program to print the number of digits required for maximum and minimum values double can take for any system:

#include <float.h>
#include <stdio.h>

int main(void)
{
    double m = DBL_MAX;
    double n = DBL_MIN;
    int i;
    i = printf("%.20g\n", m);
    printf("%d\n", i);
    i = printf("%.20g\n", n);
    printf("%d\n", i);
    return 0;
}

For me, it prints:

1.7976931348623157081e+308
27
2.2250738585072013831e-308
27

Since 27 includes a newline but doesn't include the terminating 0 for the strings, I would say that on this system, 27 should suffice. For long double, the answer seems to be 27 and 28 for LDBL_MAX and LDBL_MIN respectively on my system.

The man page (on my for sprintf says this about %g:

The double argument is converted in
style f or e (or F or E for G
conversions). The precision specifies the number of
significant
digits. If the precision is missing, 6 digits are given; if the
precision is zero, it is treated as 1. Style e is used if the
exponent from its conversion is less than -4 or greater
than or
equal to the precision. Trailing zeros are removed from the
fractional part of the result; a decimal point appears only
if it
is followed by at least one digit.

Similar wording is in the C standard.

So I think you will be safe if you used the output from the above program as your array size.

好多鱼好多余 2024-08-22 15:31:50

如果您使用的平台支持 POSIX 或 C99,则应该能够使用 snprintf 来计算您需要的缓冲区的大小。 snprintf 接受一个参数,指示您传入的缓冲区的大小;如果字符串的大小超过该缓冲区的大小,它会截断输出以适合缓冲区,并返回适合整个输出所需的空间量。您可以使用它的输出来分配大小正确的缓冲区。如果您只想计算所需的缓冲区大小,则可以传入 NULL 作为缓冲区和大小 0 来计算需要多少空间。

int size = snprintf(NULL, 0, "%.20g", x);
char *buf = malloc(size + 1); // Need the + 1 for a terminating null character
snprintf(buf, size + 1, "%.20g", x);

使用后请记住free(buf)以避免内存泄漏。

这样做的问题是它无法在 Visual Studio 中运行,它仍然不支持 C99。虽然他们有 类似 snprintf ,如果传入的缓冲区太小,它不会返回需要的大小,而是返回-1,这是完全没有用的(并且它不接受NULL< /code> 作为缓冲区,即使长度为 0)。

如果您不介意截断,则可以简单地将 snprintf 与固定大小的缓冲区一起使用,并确保不会溢出它:

char buf[30];
snprintf(buf, sizeof(buf), "%.20g", x);

请确保在 snprintf< 上检查您的平台文档/代码>;特别是,如果字符串被截断,某些平台可能不会在字符串末尾添加终止 null,因此您可能需要自己执行此操作。

If you are on a platform that supports POSIX or C99, you should be able to use snprintf to compute the size of the buffer you will need. snprintf takes a parameter indicating the size of the buffer you are passing in; if the size of the string would exceed the size of that buffer, it truncates the output to fit into the buffer, and returns the amount of space it would have needed to fit the entire output. You can use the output of this to allocate a buffer that's the exact right size. If you just want to compute the size of the buffer you need, you can pass in NULL as the buffer and a size of 0 to compute how much space you need.

int size = snprintf(NULL, 0, "%.20g", x);
char *buf = malloc(size + 1); // Need the + 1 for a terminating null character
snprintf(buf, size + 1, "%.20g", x);

Remember to free(buf) after you've used it to avoid memory leaks.

The problem with this is that it won't work in Visual Studio, which still does not support C99. While they have something like snprintf, if the buffer passed in is too small, it does not return the size needed, but returns -1 instead, which is completely useless (and it does not accept NULL as a buffer, even with a 0 length).

If you don't mind truncating, you can simply use snprintf with a fixed size buffer, and be assured that you won't overflow it:

char buf[30];
snprintf(buf, sizeof(buf), "%.20g", x);

Make sure you check your platform docs on snprintf; in particular, some platforms may not add a terminating null at the end of the string if the string is truncated, so you may need to do that yourself.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文