Печать числа с плавающей запятой в C, избегая двойного продвижения параметра - PullRequest
8 голосов
/ 02 апреля 2011

Как я могу напечатать (то есть в stdout) float в C без , если он будет преобразован в double при передаче в printf?

Проблема здесь в том, что функции с переменным числом в C переводят все параметры float в double , что приводит к двум ненужным преобразованиям.Например, если вы включите -Wdouble-promoing в GCC и скомпилируете

float f = 0.f;
printf("%f", f);

, вы получите

warning: implicit conversion from 'float' to 'double' when passing argument to function

У меня относительно небольшая вычислительная мощность, чтобы игратьс (72 МГц ARM Cortex-M3), и я определенно являюсь узким местом в ASCII-выводе данных с плавающей запятой.Поскольку архитектуре не хватает аппаратного FPU для начала, необходимость преобразования между одинарной и двойной точностью не помогает.

Есть ли способ более эффективно напечатать плавающее число в прямом C?

Ответы [ 4 ]

9 голосов
/ 02 апреля 2011

Отказ от промоушена ничего не спасет, поскольку внутренняя double (или, скорее всего, long double) арифметика printf будет занимать как минимум в 1000 раз больше времени. Точная печать значений с плавающей запятой не просто .

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

Или вы можете просто сделать что-то вроде:

printf("%d.%.6d", (int)x, (int)((x-(int)x)*1000000));
5 голосов
/ 02 апреля 2011

К сожалению, printf не поддерживает передачу обычного числа с плавающей точкой: s.

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

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

2 голосов
/ 02 апреля 2011

Я думаю, что это не имеет значения - printf - уже такая отвратительная вещь, отнимающая много времени, что это преобразование не должно иметь значения.Время преобразования числа с плавающей точкой в ​​двойное должно быть намного меньше, чем преобразование любого числа в ascii (вы должны / могли бы профилировать свой код, чтобы получить определенный ответ).Единственное оставшееся решение - написать собственную пользовательскую процедуру вывода, которая преобразует float-> ascii, а затем использует put (или аналогичные).

0 голосов
/ 02 апреля 2011
  • Первый подход: используйте ftoa вместо printf. Профиль.

  • Для повышения гибкости вывода я бы пошел в исходный код stdlib вашего компилятора, возможно, в любом случае, производного от gcc, нашел бы реализацию printf и скопировал соответствующий код для преобразования double -> ascii. Перепишите его, чтобы всплыть -> ASCII.

  • Затем вручную измените один или два выдающихся колл-сайта на новую (не вариационную) версию и профилируйте ее.

  • Если это решит вашу проблему, вы можете подумать о переписывании своего собственного printf на основе версии из stdlib, в которой вместо float вы передаете float *. Это должно избавиться от автоматического продвижения.

...