我正在将double值转换为字符串,如下所示:
std::string conv(double x) {
char buf[30];
sprintf(buf, "%.20g", x);
return buf;
}
我已经将缓冲区大小硬编码为30,但我不确定它是否足够大以适应所有情况。
PS:出于效果原因,我无法使用ostringstream
或boost::lexical_cast
(请参阅this)
答案 0 :(得分:3)
printf("%.20g", 1.79769e+308);
是1.7976900000000000632e+308
,27个字节,包括尾随\ 0。我会选择64或128只是为了确定。
(因为它在堆栈上并且在您也可以使用大缓冲区之后立即释放,甚至是2048字节,而不会遇到非嵌入式应用程序的问题)
另外,您确定程序的瓶颈是lexical_cast
..?做你正在做的事对我来说似乎很愚蠢
答案 1 :(得分:3)
我似乎记得,如果您使用sprintf
目的地致电NULL
,它就不会做任何事情。但是,它确实返回了“写”的字符数。如果我是对的(我似乎无法找到它的来源),那么你可以这样做:
// 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
将缓冲区的大小作为参数,并且不允许输出字符串超过该大小。
答案 2 :(得分:3)
我已将缓冲区大小硬编码为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个字符的缓冲区就足够了。
答案 3 :(得分:0)
这是一个程序,用于打印任何系统可以采用的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_MAX
和LDBL_MIN
的答案似乎分别是27和28。
手册页(在我的sprintf
上说明了%g
:
转换双参数 样式f或e(或G的F或E) 转换)。精度指定的数量 重大 数字。如果精度丢失,则给出6位数字;如果 精度为零,它被视为1.如果是,则使用样式e 转换的指数小于-4或更大 比或 等于精度。尾随零从中移除 结果的小数部分;小数点仅出现 如果它 之后至少有一位数。
类似的措辞在C标准中。
因此,如果您使用上述程序的输出作为数组大小,我认为您将是安全的。
答案 4 :(得分:0)
如果您使用的是支持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。虽然它们有something like snprintf
,但如果传入的缓冲区太小,则不会返回所需的大小,而是返回-1
,这完全没用(并且它不接受NULL
作为缓冲区,即使长度为0
。
如果你不介意截断,你可以简单地使用snprintf
一个固定大小的缓冲区,并确保你不会溢出它:
char buf[30];
snprintf(buf, sizeof(buf), "%.20g", x);
确保在snprintf
上查看您的平台文档;特别是,如果字符串被截断,某些平台可能不会在字符串末尾添加终止空值,因此您可能需要自己执行此操作。