C 找出字段长度为%f";sprintf中的格式

C 找出字段长度为%f";sprintf中的格式,c,floating-point,c99,printf,C,Floating Point,C99,Printf,在库的深处,我需要一个函数来分配字符串,将提供的浮点数写入%f格式的字符串,然后返回它。snprintf()返回所需的字符数,这将允许我通过2个snprintf()调用分配正确的大小。不幸的是,这是一个性能关键的部分,因此我希望避免这种情况,因为*printf()可能会很慢(是的,在一些基准测试中,它确实显示在配置文件的#1处) 另一种方法是使用一些C99函数来解决这个问题,但是libm调用也需要相当长的时间。我的函数的(非常简化,没有错误处理等)版本如下所示 //返回一个字符串,该字符串包含

在库的深处,我需要一个函数来分配字符串,将提供的浮点数写入%f格式的字符串,然后返回它。snprintf()返回所需的字符数,这将允许我通过2个snprintf()调用分配正确的大小。不幸的是,这是一个性能关键的部分,因此我希望避免这种情况,因为*printf()可能会很慢(是的,在一些基准测试中,它确实显示在配置文件的#1处)

另一种方法是使用一些C99函数来解决这个问题,但是libm调用也需要相当长的时间。我的函数的(非常简化,没有错误处理等)版本如下所示


//返回一个字符串,该字符串包含%f格式的数字x,小数点后有d位数字。
char*my_函数(双x,int d)
{
intn=ceil(log10(pow(2,ilogb(x)));
//3个额外字符,初始符号,“.”和终止null。
char*s=malloc(n+d+3);
snprintf(s,n+d+3,“%#-+.*f”,d,x);
返回s;
}

一种可能更快的方法是分配一个“足够大”的字符串,然后只有在不太可能发生的情况下才执行第二个snprintf调用,结果证明它太短

也可以对代码进行结构化,以便我可以将堆栈分配与alloca()和/或C99 VLA一起使用,但由于堆栈空间通常非常有限,我希望避免在最坏情况下大小的缓冲区上占用大量内存


有更好的主意吗?

您是在内存受限的系统中运行的吗?如果您还没有达到真正需要计算字节的程度,只需确定最坏的情况,并将所有分配设置为这个大小

另一个选项是让调用者提供缓冲区


最后,您知道您是否可以声明不需要同时存在超过有限数量的这些值?如果是这样,请为它们预先分配空间,不要在函数中执行malloc()。既然您说性能至关重要,那么您也应该尽量避免malloc()。

您知道普通malloc()实现不能分配少于32个字节吗。 因此,您可能不应该关心字符串的确切大小

如果您的程序是时间关键型的/或具有内存限制,请使用交换格式%a(%a),字符串的最大长度将非常小(-0x1.ffffffffffffp+1023是最小负值),并使用预先分配的插槽(使用专用的板分配器).

正如您在评论中所说:我希望快速处理常见案例,并更正“愚蠢”案例(如,%f格式中的值非常大)。

以下是一个解决方案:

char *function (double x, int d)
{
    ssize_t ret;
    size_t size = 32;
    char *buffer = malloc(size);

    ret = snprintf(buffer, size, "%#-+.*f", d, x);
    if (ret < size) {
        return buffer;
    }

    size = ret + 1;
    buffer = realloc(buffer, size); 
    snprintf(buffer, size, "%#-+.*f", d, x);

    return buffer;
}
char*函数(双x,int d)
{
ssize_t ret;
尺寸=32;
char*buffer=malloc(大小);
ret=snprintf(缓冲区,大小,“%#-+.*f”,d,x);
如果(ret<尺寸){
返回缓冲区;
}
尺寸=ret+1;
缓冲区=realloc(缓冲区,大小);
snprintf(缓冲区,大小,“%#-+.*f”,d,x);
返回缓冲区;
}
32字节是malloc()完成的最小分配的常见大小,请求更少不会给您带来任何好处。
您可以调整默认大小以匹配要转换的公共值。

我可以构造代码,以便使用堆栈分配(alloca()),从而从等式中删除malloc性能。但在这种情况下,我希望避免在最坏情况下大小的缓冲区上浪费大量堆栈空间。否则,内存并不是特别有限。如果要使用堆栈分配,则无法将指针返回到调用方——一旦返回,在当前堆栈帧期间分配的指针将不再有效。但是,您可以让调用者执行分配并将指针安全地传递到函数中(假设调用者不需要将缓冲区发送回自己的调用者)。“避免在最坏情况下大小的缓冲区上浪费大量堆栈空间”-IEEE double的
n
最坏情况是308。当然,通常情况下,由于精度的损失,这些数字中的大多数毫无意义。您的函数不是递归的,所以这需要在多小的系统上运行,而您认为您不能使用300字节的堆栈?即使
d
变大,双精度在终止前的小数位数也有一个上限。它与有效位的位数相同,可能加上1,所以是50左右。这是因为1/8只有3个d.p,1/16只有4个d.p,等等@SteveJessop:实际上,我正在为不同的参数类型生成此函数的几个版本,包括四精度。而且,即使大多数数字都是无意义的(至少对于一些无意义的定义而言),我当然不希望结果被截断,因为我使用的是%f格式,因此截断可能会改变结果的大小。@janneb:这可以解释为什么代码如此热门,如果您正在为一个大的四精度值生成5k个非常无用的十进制数字:-(。即使如此:“一个系统有多小…堆栈的5k”?是的,%a,%e和%g格式很简单。但是我们也需要支持%f,这是一个PITA:-/不要比较%e,%g和%a:使用%a,printf()不需要做很多计算,与%e,%g相比,它更简单、更快、更节省空间……但对于未经训练的眼睛来说可能很难阅读。我比较它们的意思是,很容易预先知道需要分配多少空间。我当然知道%a的格式化速度比%e或%g快得多。但不管怎样问题是关于我们必须支持的%f,无论是好是坏,都不支持“如果”或“但是”。对于%a格式的值“-0x1.fffffffffffff p+1023”,您的代码使用%.12f格式给出"-1797693134862315708145274237317043567980705675258449965989174768031572607800285387605895586327668781715404589535143824642343213268894641827684675467035375169860499105765512820762454900903893289440758685
char *function (double x, int d)
{
    ssize_t ret;
    size_t size = 32;
    char *buffer = malloc(size);

    ret = snprintf(buffer, size, "%#-+.*f", d, x);
    if (ret < size) {
        return buffer;
    }

    size = ret + 1;
    buffer = realloc(buffer, size); 
    snprintf(buffer, size, "%#-+.*f", d, x);

    return buffer;
}