Подтвердить что ты не робот

Преобразование double в char * в С++ с высокой производительностью

Моему приложению нужно преобразовать двойные значения в char * для записи в канал, который принимает только символы. Обычные способы сделать это - использовать функцию sprintf() или использовать ostringstream из файла заголовка iomanip.h.

Оказывается, обе они имеют очень плохую производительность. И мое приложение должно делать это преобразование настолько часто, что оно становится основным узким местом.

Есть ли какая-нибудь другая функция, которую я мог бы использовать? Какую логику я могу использовать для написания эффективной функции преобразования? Единственное, что мне удалось найти до сих пор, - это получить каждую отдельную цифру, используя операции разделения и мод, и добавить эти цифры к char *, чтобы получить все двойное значение. Однако это не похоже на хороший подход и, скорее всего, будет иметь плохую производительность.

Заранее благодарим за ваши мысли.

EDIT: существует некоторая путаница в отношении того, как будет использоваться char *. char * будет аргументом функции fwrite, которая записывает в трубу.

4b9b3361

Ответ 1

Если вы хотите напечатать любой номер, который может поддерживать двойной тип, используйте любую библиотеку для выполнения задания. Это экономит ваше здравомыслие: Почему" dtoa.c " содержат столько кода?

Если вы хотите напечатать подмножество чисел в двойном типе. Например, до десяти цифр после десятичной точки и не более 5 цифр до десятичной точки, вы можете округлить число и преобразовать в тип int, прежде чем распечатывать его с помощью деления и мода. Я могу подтвердить эффективность этого метода.


EDIT: Если первоначальная цель заключается в отправке данных для связи, то отправка двоичной формы double будет самым быстрым и точным методом (без потери точности из-за преобразования). Способ сделать это объясняется в других ответах.

Ответ 2

если вы хотите прочитать байты данных байтом. используйте эту технику

double dbl = 2222;
char* ptr = (char*)(&dbl);

это возвращает младший байт dbl. и ptr ++ будет ссылаться на второй байт dbl.

Ответ 3

Вы можете использовать методы std:: ostream write и std:: istream read с любым типом данных, который вам просто нужно переинтерпретировать_качать данные как указатель char:

double value = 5.0;
std::ostream os;
//...
os.write(reinterpret_cast<const char*>(&value), sizeof(value));
//..
std::istream is;
is.read(reinterpret_cast<char*>(&value), sizeof(value));

Ответ 4

Вы контролируете оба конца трубы? Вы просто пытаетесь пропустить туннели или вам нужно правильное текстовое представление двойника?

Если вы просто туннелируете, а труба чиста 8 бит, используйте один из приведенных выше ответов.

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

Если ваша проблема в том, что канал имеет ширину всего 7 бит, а затем конвертируйте в radix 64 для записи и обратно при чтении.

Ответ 5

Некоторые системы предоставляют функции преобразования dtostre и dtostrf - могут стоить бенчмаркинга. Вы можете посмотреть исходный код sprintf() (например, версию GNU) для идей, подгонять форматирование %e, %f и/или %g по желанию, избегая шага интерпретации строки форматирования и даже делая его проницаемым, и вы можете удалить специальный корпус для NaN, бесконечности и других значений, если знаете, что вам не нужно их обрабатывать.

Ответ 6

Медленная часть sprintf в вашей системе может не обязательно преобразовывать double, а синтаксический разбор строки формата. Это может быть хорошей новостью для вас, поскольку это то, что вы могли бы оптимизировать.

Кроме того, старайтесь документировать все знания о диапазоне, точности и характере значений double, которые необходимо обработать, и использовать его для разработки специального алгоритма.

Предполагая, что ваши входы никогда не являются субнормальными числами, используйте известную фиксированную точность и точность, относительно высокий результат может выглядеть следующим образом:

itoa((int)((f + 0.00001) * 10000))

Однако подходы sprintf и ostream, о которых вы уже знаете, являются единственными решениями, которые являются полностью универсальными и портативными.

Ответ 7

/*

_ecvt_s Converts a double number to a string.

Syntax:

errno_t _ecvt_s( 
   char * _Buffer,
   size_t _SizeInBytes,
   double _Value,
   int _Count,
   int *_Dec,
   int *_Sign
);

[out] _Buffer
Filled with the pointer to the string of digits, the result of the conversion.

[in] _SizeInBytes
Size of the buffer in bytes.

[in] _Value
Number to be converted.

[in] _Count
Number of digits stored.

[out] _Dec
Stored decimal-point position.

[out] _Sign
Sign of the converted number.


*/


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

...
char *buf = (char*) malloc(_CVTBUFSIZE);
int decimal;
int sign;
int err;


err = _ecvt_s(buf, _CVTBUFSIZE, 1.2, 5, &decimal, &sign);

if (err != 0) {
// implement error handling
}
else printf("Converted value: %s\n", buf);

...

Надеюсь, что это поможет.

Ответ 8

Использование ftoa будет немного лучше, чем sprintf, поскольку это то, что он использует внутри. См. Соответствующий вопрос здесь. Посмотрите также, как ftoa реализована в вашем источнике библиотеки и посмотрите, можете ли вы улучшить его для своих конкретных сценариев.

Кажется, что ftoa совсем не стандарт, мой плохой. Здесь обсуждение, показывающее реализацию, которая утверждает, что она намного быстрее, чем sprintf. Вам нужно будет профилировать оба в ваших собственных целевых средах и реализовать с помощью double, а не float.