LINUX.ORG.RU

float -> char[], размер буфера

 ,


1

3

Подскажите пожалуйста как просчитать размер буфера (N) в compile-time для конверсии float/double/long double -> char[N]. Примерный код для конверсии приведен ниже [1]

Сам буффер char[N] будет находится в POD структуре, т.е. его размер надо знать уже во время компиляции. Подскажите пожалуйста, как посчитать размер буфера в таких условиях? (есть ли смысл использовать numeric_limits10<T>::max_digits10 + X?)

[1] Конверсию планируется делать как-то так:

template <typename Decimal>
char* convertToBuffer(Decimal d, 
                      char *buf, 
                      std::size_t size,
                      std::size_t precision);

template <> 
char* convertToBuffer(float d, 
                      char *buf, 
                      std::size_t size,
                      std::size_t precision) 
{
    auto written = std::snprintf(buf, size, "%.*f", precision, d);
    if (written < 0)
        throw std::runtime_error("conversion went wrong");

    // %g не используется, ибо конверсия предпочтительно в виде %f
    if (written >= size) {
        written = std::snprintf(buf, size, "%e", d);
        if (written < 0)
            throw std::runtime_error("conversion went wrong");
    }

    return buf;
}

★★★★★

Последнее исправление: KennyMinigun (всего исправлений: 1)

Ответ на: комментарий от asaw

Ах да, забыл добавить: sizeof.  — Ваш К.О.

asaw ★★★★★
()

Писать шаблоны, эксепшены и прочие радости и не знать про еще Cишный sizeof ? А, тебе в строку конвертить. Тогда для целых - max_digits10, для float - выделять сколько хочется и ограничивать точность в sprintf

CatsCantFly
()
Последнее исправление: CatsCantFly (всего исправлений: 1)
Ответ на: комментарий от CatsCantFly

А, тебе в строку конвертить.

И правда... Странные люди индейцы. И говорят странно.

asaw ★★★★★
()

Не получится. Размер буфера можно посчитать через snprintf(nullptr, 0, fmt, args...), но результат не constexpr.

thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 1)
Ответ на: комментарий от CatsCantFly

Тогда для целых - max_digits10

Вот только (напр) numeric_limits<int>::max_digits10 = 0. Ну да ладно, наверное имелось ввиду digits10.

для float - выделять сколько хочется и ограничивать точность в sprintf

Вот собственно об том и вопрос: сколько бы «хотелось» (как минимум, чтобы было достаточно)? При том, что точность передается параметром шаблона (как size_t).

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от slovazap

Господи, зачем в C++ использовать char*, snprintf и фиксированные буфферы?

Любой может в std::to_string() Просто надо, чтоб буфер был на стеке (меньше алокаций динамической памяти).

snprintf

Знаешь что-то лучше и удобнее? (учитывая условия)

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

в compile-time
snprintf

надо, чтоб буфер был на стеке

Партизаны становились всё гуще и гуще.

Размер буффера — в compile-time.

И чем тебе не подошла константа?

inb4: размер буфера должен варьироваться в зависимости от значения float/double.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

размер буфера должен варьироваться в зависимости от значения float/double.

Нужно просто воспользоваться scientific-форматом.

utf8nowhere ★★★
()
Ответ на: комментарий от KennyMinigun

Просто надо, чтоб буфер был на стеке

Храните в double, делов то. Всё равно у вас runtime точность, по ней и аллоцируйте из стека в runtime.

vodz ★★★★★
()
Ответ на: комментарий от utf8nowhere

Тогда бы он не создавал такой топик на ЛОРе.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

inb4: размер буфера должен варьироваться в зависимости от значения float/double.

Спасибо, кеп.

И чем тебе не подошла константа?

Вполне себе подходит. Только лучше, чтоб не просто 25. А max_digits10 + что-то.

надо, чтоб буфер был на стеке

Партизаны становились всё гуще и гуще.

Поскольку я больше не доверяю твоему воображению, вот пример:

template <typename T>
constexpr size_t getBufferSize();

template <>
constexpr size_t getBufferSize<float>() {
    // вот тут более обоснованная константа
    return std::numeric_limits<float>::max_digits10 + 20; 
}

template <typename T, size_t precision>
struct NumericView {
    char chars[getBufferSize<T>() + precision];
    // ... конструкоры и т.п.
};

int main() {
    NumericView<float, 2> pi(3.14);
    std::cout << pi.buf << std::endl;
}

* написано с телефона

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от thunar

Получится, если ограничиться экспоненциальным форматом. Тогда размер буфера можно прикинуть на основе max_digits10, min/max_exponent10 плюс константа, включающая в себя знак мантиссы и порядка, десятичную точку и ещё что-нибудь.

utf8nowhere ★★★
()
Ответ на: комментарий от utf8nowhere

если ограничиться экспоненциальным форматом.

Искусственное ограничение. 64 бита ≈ 18.5 квинтиллионов.

Тогда размер буфера можно прикинуть

В этом нет нужды, он на стэке.

LamerOk ★★★★★
()

как вариант:

N = (sizeof(d)*3)/8 + Х;

Это будет с небольшим запасом но пара байт оверхеда на современных гигабайтах никого волновать не будут. Я думаю ты понял мой ход мыслей.

cvv ★★★★★
()
Последнее исправление: cvv (всего исправлений: 2)
int len = snprintf(NULL, 0, ...);
char buf[len + 1];
len = snprintf(buf, sizeof(buf), ...);

Не совсем кроссплатформенно (как минимум MSVC не умеет массивы переменной длины, GCC и Clang умеют), но будет делать именно то, что ты хочешь - буфер выделяется на стеке, его гарантированно хватит.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от KivApple

Да, для такого есть alloca, хоть не самый красивый вариант, зато более портабельный.

Но такой код несовсем вписывается в дизайн, где NumericView инстанциируется типом. Вот именно хочется чтоб буфер был еще и фиксированный:

template <typename Decimal, std::size_t precision = 6>
class NumericView {
public:    
    NumericView(Decimal value) {
       convertToBuffer(value, buffer.data(), buffer.size(), precision);
    }

private:
    constexpr std::size_t BUFFER_SIZE = std::is_integral<Decimal>::value 
               ? std::numeric_limits<Decimal>::digits10 + 3
               : std::numeric_limits<Decimal>::max_digits10 + X;

    std::array<char, BUFFER_SIZE> buffer;
};

Но вообще да, быть может такой подход неоправданный. Буду завтра курить IEEE 754-2008, может придёт в голову что-то более-менее адекватное

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

вроде как стандарт C++ не определяет бинарное представление float/double. А если так, то максимальное по размеру памяти подсчитать не представляется возможным переносимым способом.

dzidzitop ★★
()
Ответ на: комментарий от dzidzitop

Я это понимаю, просто хочется какое-то более менее-вменяемое число по умолчанию (с возможностью внешней регуляции естественно), чтоб покрыть так называемые «80% случаев». А для крайностей — экспонентная форма.

KennyMinigun ★★★★★
() автор топика
Ответ на: комментарий от KennyMinigun

просто хочется какое-то более менее-вменяемое число по умолчанию

42. just for lulz. Если покажется мало, разрешаю использовать 64, 92, 128 и так далее.

kawaii_neko ★★★★
()

http://www.stroustrup.com/Tour.html

и уже больше могущим в ру чем в анг есть

Страуструп Б. Язык программирования С++ (стандарт С++11). Краткий курс. ISBN 978-5-9518-0699-4 Кол-во страниц 176

там явно есть ответы на беспокоящие тебя фобии

anonymous
()

Что-то из <float.h>? Например, FLT_MAX_10_EXP. Накинуть еще единицу, т.к. оно для обратной операции предназначено. Добавить "-", ".". И всё. А «+ precision» не надо, оно в FLT_MAX_10_EXP уже есть.

fopen ★★
()
Ответ на: комментарий от KennyMinigun

хотя судя по этому http://www.cplusplus.com/reference/cfloat/ может и можно подумать и подсчитать для конкретной базы самое широкое представление - судя по всему всё-таки экспоненциальный бинарный формат гарантируется. но чтоб формулу вывести точно нужно мозгу напрячь.

dzidzitop ★★
()
Ответ на: комментарий от fopen

MAX_10_EXP не будет работать. Скорее нужно с MIN_10_EXP работать - минимальное число будет занимать больше места чем максимальное.

dzidzitop ★★
()
Ответ на: комментарий от CatsCantFly

Тогда для целых - max_digits10, для float - выделять

/0

anonymous
()
Ответ на: комментарий от KennyMinigun

в общем надо найти min n при котором

(FLT_RADIX ^ FLT_MIN_EXP) * (base ^ (n - 2)) >= 1

(2 ^ FLT_MANT_DIG) * (FLT_RADIX ^ FLT_MIN_EXP) / (base ^ (n - 3)) <= 1

n - 2 - задел под "-."

n - 3 - задел под "-.0"

base - основание числа в строке. задаётся извне.

вроде как-то так. на 32bit float можно полным перебором проверить.

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 1)
Ответ на: комментарий от KennyMinigun

Ну и как бы стоит добавить, что вывод float зависит от текущей локали (например, десятичный разделитель). В compile-time ты его не узнаешь. Выдели 100 и не парь людям мозг.

CatsCantFly
()
Ответ на: комментарий от CatsCantFly

Выдели 100 и не парь людям мозг.

Вот за что «люблю» сишников, так это за char[2000], int[50] и т.д. Ничего личного. Просто это самое характерное для них. При том, что в большинстве случаев константы выбраны с заделом на проведение атак типа «переполнение буфера».

dzidzitop ★★
()
Ответ на: комментарий от CatsCantFly

Хозяйкам на заметку:

unless (defined &__LDBL_MAX_10_EXP__) { sub __LDBL_MAX_10_EXP__() { 4932 } }

100 маловато будет :)

dzidzitop ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.