Есть ли printf
спецификатор ширины, который можно применить к спецификатору с плавающей запятой, который автоматически форматировал бы вывод до необходимого количества значащих цифр , чтобы при сканировании строки обратно было получено исходное значение с плавающей запятой?
Например, предположим, что я печатаю float
с точностью до 2
десятичных знаков:
float foobar = 0.9375;
printf("%.2f", foobar); // prints out 0.94
Когда я просматриваю вывод 0.94
, у меня нет соответствующей стандартам гарантии, что я верну исходное значение с 0.9375
плавающей запятой (в этом примере я, вероятно, не буду).
Я хотел бы указать способ printf
автоматической печати значения с плавающей запятой до необходимого количества значащих цифр, чтобы его можно было сканировать обратно до исходного значения, переданного printf
.
Я мог бы использовать некоторые из макросов, float.h
чтобы получить максимальную ширину для перехода printf
, но есть ли уже спецификатор для автоматической печати до необходимого количества значащих цифр - или, по крайней мере, до максимальной ширины?
источник
printf( "%f", val );
что уже переносимо, эффективно и по умолчанию.double
. Поскольку вашdouble
становится очень большим (очень далеко от 1.0), он становится менее точным в десятичной части (часть значения меньше 1.0). Итак, у вас не может быть здесь удовлетворительного ответа, потому что ваш вопрос содержит ложное предположение (а именно, что всеfloat
с /double
с созданы равными)Ответы:
Я рекомендую шестнадцатеричное решение @Jens Gustedt: используйте% a.
OP хочет «печатать с максимальной точностью (или, по крайней мере, до самого старшего десятичного числа)».
Простой пример - напечатать одну седьмую, как в:
Но давайте копнем глубже ...
Математически ответ будет «0,142857 142857 142857 ...», но мы используем числа с плавающей запятой конечной точности. Предположим, IEEE 754 двоичный код двойной точности . Итак,
OneSeventh = 1.0/7.0
результаты в значении ниже. Также показаны предыдущие и последующие представимыеdouble
числа с плавающей запятой.Печать точного десятичного представления a
double
имеет ограниченное применение.В C есть 2 семейства макросов, которые
<float.h>
могут нам помочь.Первый набор - это количество значащих цифр для печати в строке в десятичном формате, поэтому при обратном сканировании строки мы получаем исходную плавающую точку. Они показаны с минимальным значением спецификации C и образцом компилятора C11.
Второй набор - это количество значащих цифр, которые строка может быть отсканирована в число с плавающей запятой, а затем напечатана FP, все еще сохраняя то же представление строки. Они показаны с минимальным значением спецификации C и образцом компилятора C11. Я считаю доступным до C99.
Первый набор макросов, похоже, соответствует цели OP по значащим цифрам. Но этот макрос не всегда доступен.
«+3» было сутью моего предыдущего ответа. Он сосредоточен на том, зная, что строка-FP-строка двустороннего преобразования (набор макросов № 2, доступных C89), как можно определить цифры для FP-string-FP (установить макрос № 1, доступный после C89)? В общем, результат прибавил 3.
Теперь известно, сколько значащих цифр нужно напечатать
<float.h>
.Для вывода N значащих десятичных цифр можно использовать различные форматы.
С
"%e"
, то точность поле количество цифр после свинцовой цифр и десятичной точки. Так- 1
что по порядку. Примечание: этого-1
нет в начальномint Digs = DECIMAL_DIG;
С
"%f"
, то точность поле количество цифр после десятичной точки. Для такого числаOneSeventh/1000000.0
нужноOP_DBL_Digs + 6
видеть все значащие цифры.Примечание: многие используются
"%f"
. Это отображает 6 цифр после десятичной точки; По умолчанию отображается 6, а не точность числа.источник
%f
- 6."%f"
в первую очередь. Использование,"%e"
как вы показали, конечно, лучший подход во всех отношениях и, по сути, достойный ответ (хотя, возможно, это не так хорошо, как использование"%a"
могло бы быть, если оно доступно, и, конечно,"%a"
должно быть доступно, если есть `DBL_DECIMAL_DIG). Мне всегда хотелось иметь спецификатор формата, который всегда округлял бы точно до максимальной точности (вместо жестко запрограммированных 6 десятичных знаков).Краткий ответ на печать чисел с плавающей запятой без потерь (таким образом, чтобы их можно было прочитать обратно с точно таким же числом, кроме NaN и Infinity):
printf("%.9g", number)
.printf("%.17g", number)
.НЕ используйте
%f
, так как это только указывает, сколько значащих цифр после десятичной дроби, и будет усекать маленькие числа. Для справки можно найти магические числа 9 и 17,float.h
которые определяютFLT_DECIMAL_DIG
иDBL_DECIMAL_DIG
.источник
%g
спецификатор?double
значения чуть выше0.1
:1.000_0000_0000_0000_2e-01
,1.000_0000_0000_0000_3e-01
потребность 17 цифр , чтобы отличить."%.16g"
недостаточно , и"%.17g"
и"%.16e"
являются достаточными. Детали%g
, которые я забыл.Если вас интересует только бит (или шестнадцатеричный шаблон), вы можете использовать
%a
формат. Это гарантирует вам:Я должен добавить, что это доступно только с C99.
источник
Нет, такого спецификатора ширины printf для печати с плавающей запятой с максимальной точностью не существует . Позвольте мне объяснить почему.
Максимальная точность
float
иdouble
является переменной и зависит от фактического значения изfloat
илиdouble
.Напомним
float
иdouble
хранятся в формате sign.exponent.mantissa . Это означает, что для дробной составляющей малых чисел используется намного больше битов, чем для больших чисел.Например,
float
можно легко различить 0,0 и 0,1.Но
float
понятия не имеет о разнице между1e27
и1e27 + 0.1
.Это связано с тем, что вся точность (которая ограничена количеством битов мантиссы) используется для большей части числа слева от десятичной дроби.
%.f
Модификатор просто говорит , сколько десятичных значений вы хотите напечатать из числа поплавка, насколько форматирование идет. Тот факт, что доступная точность зависит от размера числа , зависит от вас, как от программиста .printf
не может / не справляется с этим за вас.источник
float
предоставляет a , и вы утверждаете, что такого нет (то есть, что нетFLT_DIG
), что неверно.FLT_DIG
ничего не значит. Этот ответ утверждает, что количество доступных десятичных знаков зависит от значения внутри числа с плавающей запятой .Просто используйте макросы from
<float.h>
и спецификатор преобразования переменной ширины (".*"
):источник
printf("%." FLT_DIG "f\n", f);
%e
, а не для%f
: только если известно, что значение для печати близко к1.0
.%e
печатает значащие цифры для очень маленьких чисел и%f
не печатает . напрx = 1e-100
.%.5f
отпечатки0.00000
(полная потеря прецессии).%.5e
печатает1.00000e-100
.FLT_DIG
определяется значением, для которого он определен по какой-то причине. Если это 6, это потому, чтоfloat
не может содержать более 6 цифр точности. Если распечатать его с помощью%.7f
, последняя цифра не будет иметь значения. Подумайте, прежде чем голосовать против.%.6f
не эквивалент, потому чтоFLT_DIG
не всегда 6. А кого волнует эффективность? Ввод-вывод уже чертовски дорог, точность до одной цифры не станет узким местом.Я провожу небольшой эксперимент, чтобы убедиться, что печать с
DBL_DECIMAL_DIG
действительно точно сохраняет двоичное представление числа. Оказалось, что для компиляторов и библиотек C, которые я пробовал,DBL_DECIMAL_DIG
действительно требуется количество цифр, и печать даже с одной цифрой меньше создает значительную проблему.Я запускаю это с компилятором Microsoft C 19.00.24215.1 и gcc версии 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1). Использование одной десятичной цифры уменьшает вдвое количество сравниваемых чисел, которые точно равны. (Я также подтвердил, что
rand()
при использовании действительно получается около миллиона различных чисел.) Вот подробные результаты.Microsoft C
GCC
источник
RAND_MAX == 32767
. Подумайтеu.s[j] = (rand() << 8) ^ rand();
или что-то подобное, чтобы убедиться, что все биты имеют шанс быть 0 или 1.В одном из моих комментариев к ответу я посетовал, что давно хотел каким-то образом вывести все значащие цифры в значении с плавающей запятой в десятичной форме, почти так же, как задается вопрос. Ну наконец то сел и написал. Это не совсем идеально, и это демонстрационный код, который выводит дополнительную информацию, но в основном он работает для моих тестов. Пожалуйста, дайте мне знать, если вы (то есть кто-нибудь) хотите получить копию всей программы-оболочки, которая управляет ею, для тестирования.
источник
Насколько мне известно, есть хорошо диффундирует алгоритм , позволяющий вывод на необходимое количество значащих цифр , например , что при сканировании строки обратно, исходное значение с плавающей точкой приобретаются в
dtoa.c
написанной Daniel Gay, который доступен здесь на Netlib (см также соответствующий документ ). Этот код используется, например, в Python, MySQL, Scilab и многих других.источник