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

С++, как получить "показатель с одной цифрой" с printf

Есть ли способ печати в научной нотации менее 3-х мест для показательной части числа? Форматирование 6.1 не влияет на экспоненту, а только на число:

var=1.23e-9;
printf ("%e\n", var);
printf ("%6.1e\n", var);

дает

1.230000e-009
1.2e-009

Я также пробовал это в wxWidgets с форматированием строки, но поведение такое же.

m_var->SetLabel(wxString::Format(wxT("%6.1e"),var));

Я бы хотел иметь 1.2e-9.

4b9b3361

Ответ 1

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

То, что вы делаете, - это эксплойт, основанный на том, что означает базовая математика (научная нотация): означает, что для всех действительных чисел y, y = (x) * 10 ^ (N) для некоторого целого N и некоторого x в диапазоне (-1, 1).

Итак, вы делаете следующее

void PrintScientific(double d)
{
   int exponent  = (int)floor(log10( fabs(d)));  // This will round down the exponent
   double base   = d * pow(10, -1.0*exponent);

   printf("%lfE%+01d", base, exponent);
}

Вы можете добавить все спецификаторы формата, которые вам нужны для управления # символами до, после ".". десятичное место.

НЕ забывайте о шаге округления! Вот как это работает, используя свойства base10 и логарифмы (база 10 здесь):

Пусть y = x * 10 ^ N = >
log (y) = log (x * 10 ^ N) = >
log (y) = log (x) + log (10 ^ N) = > //Из правила журнала "товар"
log (y) = log (x) + N

Так как x находится в диапазоне (-10, 10) - "()" означает эксклюзивную (эксклюзивную), что означает, что log (x) находится в диапазоне (-1, 1). Поэтому, когда мы округляем для целочисленного преобразования, мы отбрасываем вклад "log (x)". Затем вы можете получить часть "x" от исходного номера, которая позволяет выводить оригинал в любой научной нотации, которую вы хотите использовать.

Ответ 2

Согласно Wikipedia:

Показатель всегда содержит не менее двух цифр; если значение ноль, показатель равен 00. В Windows показатель состоит из трех цифр по умолчанию, например. 1.5e002, но это может быть изменено Специфическая для Microsoft функция _set_output_format.

_ set_output_format

Ответ 3

При стандартном C printf() это невозможно сделать (и использование трех цифр по умолчанию также кажется неправильным), по крайней мере, на C99 (у меня нет новой версии под рукой). Соответствующая цитата из стандарта C99 находится в пункте 7.19.6.1, пункт 8, форматы e, f:

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

Лучше всего поместить этот код в код с помощью большого количества этих выходов, чтобы использовать С++ IOStreams: хотя форматирование по умолчанию такое же, как на C, можно установить настраиваемый фасет в поток std::locale который делает форматирование так, как вам нужно. Тем не менее, писать код форматирования может быть не совсем тривиальным. Хотя я, вероятно, просто построил бы стандартное преобразование, а затем удалю лишние нули после символа e.

Ответ 4

Я обнаружил, что Zach отвечает за самый быстрый и простой метод и также применим к любой ОС. Я обнаружил, что для строки "base =" необходимы две модификации для работы для всех чисел. (В противном случае nan, когда экспонента отрицательна в cygwin). Дополнительный оператор печати предназначен только для нейтральной совместимости файлов с патентом. Я бы поддержал его ответ, но я только начал с stackexchange, поэтому у меня нет достаточной "репутации".

void PrintScientific(double d)
{
   int exponent  = (int)floor(log10( fabs(d)));  // This will round down the exponent
   double base      = (d * pow(10.0,  -1*exponent));

if(abs(exponent)<10)
    printf("%13.9lfE%+01d", base, exponent);
else
    printf("%12.9lfE%+01d", base, exponent);
}

Ответ 5

C/С++ указывает, по крайней мере, две цифры экспоненты с printf("%e",...). Для печати только 1 и для работы с Visual Studio, который по умолчанию печатает как минимум 3, необходим дополнительный код.

Рассмотрим IOStreams @Dietmar Kühl

Если код С++ по-прежнему хочет использовать форматы стиля printf():

Корректировка значения double перед вызовом printf() слишком часто приводит к проблемам округления, короткому замыканию диапазона и общим отказам в случае отказа, например, при работе с log10(0.0). Также обратите внимание на большой double чуть больше мощности 10, где log10() может оказаться коротким, -0.0, INF, NAN.

В этом случае лучше выполнить пост-обработку строки.

  double var = 1.23e-9;
  //                    - 1 . x e - EEEEE \0
  #define ExpectedSize (1+1+1+1+1+1+  5 + 1)
  char buf[ExpectedSize + 10];
  snprintf(buf, sizeof buf, "%.1e", var);
  char *e = strchr(buf, 'e');  // lucky 'e' not in "Infinity" nor "NaN"
  if (e) {
    e++;
    int expo = atoi(e);
    snprintf(e, sizeof buf - (e - buf), "%1d", expo);
  }
  printf("'%6s'\n", buf);  // '1.2e-9'

Примечание: %e любезна для последующей обработки, поскольку его ширина не столь громоздка, как "%f". sprintf(buf, "%f", DBL_MAX) может быть 1000 с char.