Компактное представление без потерь констант с плавающей точкой в ​​C / Stack Overflow

У меня есть программа, написанная на C ++, которая генерирует исходный код C для математических расчетов. Я заметил, что константы занимают очень много места в сгенерированном коде и ищут более компактное представление.

Для генерации констант я сейчас использую:

double v = ...
cfile << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1) << v;

Я почти уверен, что это представление без потерь, но оно также очень раздутое. Например, ноль и единица будут представлены как что-то вроде 0,0000000000000000e + 00 и 1.0000000000000000e + 00. И «0» или «1» несет столько же информации.

Есть ли способ напечатать константы в файл более компактным, но все же без потерь способом? Он не должен хорошо выглядеть для читателя-человека, просто компилировать, когда он представлен в простом C-коде (если C99, я бы предпочел, чтобы это также был C ++). Шестнадцатеричный может быть в порядке, если он переносим.

РЕДАКТИРОВАТЬ: Удалено std::fixed во фрагменте кода.

5

Решение

Это не проблема представления, языка или стандартной библиотеки, а алгоритма. Если у вас есть генератор кода, то … почему бы вам изменить сгенерированный код быть лучшим (= самым коротким с требуемой точностью) представления? Это то, что вы делаете, когда пишете код от руки.

В гипотетическом put_constant(double value) рутина вы можете проверьте, какое значение вы должны написать:

  • Это целое число? Не раздутый код с std::fixed а также set_precision, просто приведите к целому числу и добавьте точку.
  • Попробуйте преобразовать его в строку с настройками по умолчанию, а затем преобразовать обратно в doubleЕсли ничего не изменилось, то представление по умолчанию (короткое) достаточно хорошо.
  • Преобразуйте его в строку с вашей фактической реализацией и проверьте его длину. Если это больше чем N (см. Позже), используйте другое представление, иначе просто напишите его.

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

#define USE_L2D __int64 ___tmp = 0;
#define L2D(x) (double&)(___tmp=x)

int main(int argc, char* argv[])
{
// 2.2 = in memory it is 0x400199999999999A

USE_L2D
double f1 = L2D(0x400199999999999A);
double f2 = 123456.1234567891234567;

return 0;
}
3

Другие решения

Вы можете использовать шестнадцатеричное число с плавающей запятой (Спецификатор формата% a для printf () в C); он определен, чтобы сохранить все биты точности (C11, 7.21.6.1p8, a,A спецификаторы).

cfile << std::hexfloat << v;

Если ваш компилятор / стандартная библиотека не поддерживает hexfloat, вы можете использовать C99 %a спецификатор printf (это эквивалентно, как указано в таблице 88 C ++ 11 в разделе 22.4.2.2.2):

printf("%a", v);

Например, следующая программа действительна C99:

#include <stdio.h>
int main() {
double v = 0x1.8p+1;
printf("%a\n", v);
}

Ваш сгенерированный исходный файл не будет действительным C ++ 11, так как довольно нелепо C ++ 11 не поддерживает шестнадцатеричные литералы с плавающей точкой. Однако многие компиляторы C ++ 11 поддерживают шестнадцатеричные литералы с плавающей запятой C99 в качестве расширения.

9

Во-первых, вы противоречите себе, когда вы впервые говорите
std::scientific, а потом std::fixed, А во-вторых, ты
наверное тоже не хочу. Формат по умолчанию обычно
разработан, чтобы сделать это лучше всего. Формат по умолчанию не
есть имя или манипулятор, но это то, что вы получите, если нет других
формат был указан и может быть установлен (в случае, если другой код
установил другой формат) используя:

cfile.setf( std::ios_base::fmtflags(), std::ios_base::floatfield );

Я бы рекомендовал использовать это. (Вам все еще нужна точность,
курс.)

1

Я не уверен, что ты Можно проходите плавающие точки без потерь, как это. Плавающие точки обязательно с потерями. Хотя они могут точно представлять подмножество значений, вы не можете включать ВСЕ значимые цифры — разное оборудование может иметь разные представления, поэтому вы не можете гарантировать отсутствие потери информации. Даже если вы могли бы передать все это, поскольку значение не может быть представлено принимающим оборудованием.

Равнина ofstream :: operator<< Тем не менее, можно было бы распечатать столько цифр, сколько необходимо, поэтому нет необходимости усложнять ситуацию.

-4
По вопросам рекламы [email protected]