找出sprintf中“%f”格式的字段长度

时间:2012-02-24 12:02:17

标签: c floating-point c99 printf

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

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


// Return a string containing number x in %f format with d digits after the decimal point.
char* my_function(double x, int d)
{
    int n = ceil(log10(pow(2, ilogb(x))));
    // 3 extra chars, the initial sign, the ".", and the terminating null.
    char *s = malloc(n + d + 3);
    snprintf(s, n + d+ 3, "%#-+.*f", d, x);
    return s;
}

一种可能更快的方法是分配一个“足够大”的字符串,然后只在不太可能发生并且结果太短的情况下再进行第二次snprintf调用。

也可以构造代码,以便我可以使用alloca()和/或C99 VLA进行堆栈分配,但由于堆栈空间通常非常有限,我希望避免在最坏情况下浪费大量内存 - 大小合适的缓冲区。

有更好的主意吗?

3 个答案:

答案 0 :(得分:1)

您是否在内存受限的系统中运行?如果你还没有真正需要计算字节数,那么只需确定最坏情况并将所有分配都调整为这个大小。

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

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

答案 1 :(得分:1)

正如您在评论Find out field length of "%f" format in sprintf中所说:我希望快速使用常见案例,而“愚蠢”的案例(如%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;
}

32个字节是malloc()完成的最小分配的通用大小,请求较少不会给你任何东西。 您可以调整默认大小以匹配您要转换的公共值。

答案 2 :(得分:0)

您是否知道常见的malloc()实现不能分配少于32个字节。 所以你可能不应该关心字符串的确切大小。

如果你的程序是时间关键/或有内存限制,使用交换格式%a(%A),字符串的最大长度将非常小(-0x1.fffffffffffffp + 1023是最小负值)和使用预先分配的槽(使用专用的slab分配器)。