Узнать длину поля формата "% f" в sprintf - PullRequest
2 голосов
/ 24 февраля 2012

Так глубоко внутри библиотеки мне нужна функция, которая выделяет строку, записывает предоставленное число с плавающей запятой в строку в формате% f и возвращает ее. snprintf () возвращает количество символов, которое потребуется, что позволило бы мне выделить правильный размер с помощью 2 вызовов snprintf (). К сожалению, это в некоторой степени критично для производительности, поэтому я бы хотел избежать этого, так как * printf () может быть медленным (да, в некоторых тестах он отображается на # 1 в профиле).

Другой способ - использовать некоторые функции C99, чтобы понять это, но вызовы libm также занимают немало времени. Версия моей функции (очень упрощенная, без обработки ошибок и т. Д.) Будет выглядеть примерно так:

<code>
// 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 только в том случае, если произошло маловероятное и оказалось, что оно слишком короткое.

Также возможно структурировать код так, чтобы я мог использовать выделение стека с помощью VLA alloca () и / или C99, но так как пространство стека часто довольно ограничено, я бы хотел избежать потери большого количества памяти на худшие размер буфера.

Есть идея получше?

Ответы [ 3 ]

1 голос
/ 24 февраля 2012

Как вы сказали в комментарии Узнайте длину поля в формате "% f" в 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 (), запрос меньше не даст вам ничего. Вы можете настроить размер по умолчанию в соответствии с общими значениями, которые вы собираетесь конвертировать.

1 голос
/ 24 февраля 2012

Вы работаете в системе с ограниченным объемом памяти?Если вы не в том месте, где вам действительно нужно считать байты, просто определите ваш худший случай и сделайте все выделения такого размера.

Другой вариант - заставить вызывающую сторону предоставить буфер.* Наконец, знаете ли вы, можете ли вы утверждать, что не будет необходимости в существовании более чем конечного числа этих значений одновременно?Если это так, предварительно выделите место для них и не выполняйте malloc () внутри вашей функции.Поскольку вы говорите, что производительность критична, вам следует также избегать malloc ().

0 голосов
/ 24 февраля 2012

Знаете ли вы, что обычная реализация malloc () не может выделять менее 32 байтов. Так что вам, вероятно, не стоит беспокоиться о точном размере вашей строки.

Если ваша программа критична по времени / или имеет ограничение памяти, используйте формат обмена% a (% A), максимальная длина строки будет очень мала (-0x1.fffffffffffffp + 1023 - минимальное отрицательное значение) и использовать предварительно выделенный интервал (используя выделенный распределитель слэба).

...