Тип float c как вывести

Форматированный ввод и вывод

Форматированный вывод

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

Функция форматированного вывода printf получает в качестве аргументов строку формат и аргументы, которые необходимо вывести в соответствии с форматом, и возвращает число выведенных символов. В случае ошибки возвращает отрицательное значение и устанавливает значение ferror. Если произошло несколько ошибок, errno равно EILSEQ.
int printf (const char * format, . );

Функция проходит по строке и заменяет первое вхождение % на первый аргумент, второе вхождение % на второй аргумент и т.д. Далее мы будем просто рассматривать список флагов и примеры использования.

Общий синтаксис спецификатора формата
%[флаги][ширина][.точность][длина]спецификатор
Спецификатор – это самый важный компонент. Он определяет тип переменной и способ её вывода.

Таб. 1 Спецификатор типа.

Спецификатор Что хотим вывести Пример
d или i Целое со знаком в в десятичном виде 392
u Целое без знака в десятичном виде 7235
o Беззнаковое в восьмеричном виде 657
x Беззнаковое целое в шестнадцатеричном виде 7fa
X Беззнаковое целое в шестнадцатеричном виде, верхний регистр 7FA
f или F Число с плавающей точкой 3.4563745
e Экспоненциальная форма для числа с плавающей точкой 3.1234e+3
E Экспоненциальная форма для числа с плавающей точкой, верхний регистр 3.1234E+3
g Кратчайшее из представлений форматов f и e 3.12
G Кратчайшее из представлений форматов F и E 3.12
a Шестнадцатеричное представление числа с плавающей точкой -0xc.90fep-2
A Шестнадцатеричное представление числа с плавающей точкой, верхний регистр -0xc.90FEP-2
c Буква a
s Строка (нуль-терминированный массив букв) Hello World
p Адрес указателя b8000000
n Ничего не печатает. Аргументом должен быть указатель на signed int. По этому адресу будет сохранено количество букв, которое было выведено до встречи %n
% Два идущих друг за другом процента выводят знак процента %

Строка формата также может включать в себя следующие необязательные суб-спецификаторы: флаг, ширина, .точность и модификатор (именно в таком порядке).

Таб. 2 Флаги.

Флаг Описание
Выключка влево на заданное шириной значение
+ Явно указывать знак у числа, даже для положительных чисел
(пробел) Если знак не будет выведен, то вставляет пробел перед выводимым числом
# Когда используется вместе с o, x или X, вставляет перед числом 0, 0x или 0X
Когда используется со спецификаторами a, A, e, E, f, F, g или G, вставляет десятичную точку, даже если после неё нет десятичных знаков.
0 Вставляет нули, когда объявлен спецификатор ширины
Таб. 3 Ширина.
Ширина Описание
(число) Минимальное количество знаков, которое необходимо вывести. Если в числе меньше знаков, то вставляет пробелы (или нули)
* Ширина не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимому числу
Таб. 4 Точность.
.Точность Описание
.число Для спецификаторов целых (d, i, o, u, x, X) точность определяет минимальное количество знаков, которое необходимо вывести. Если значение короче, то выводятся нули перед числом. Значение не обрезается, даже если оно длиннее. Точночть 0 означает, что для значения 0 ничего не выводится.
Для спецификаторов чисел с плавающей точкой (a, A, e, E, f, F) это число знаков, которые необходимо вывести после десятичной точки (по умолчанию 6).
Для g и G — это число значащих разрядов, которые необходимо вывести.
Для s — выводится указанное число символов. По умолчанию выводятся все символы до первого нулевого.
Если число не стоит, то по умолчанию точность равна 0
.* Точность не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимому числу

Суб-спецификатор длины изменяет длину типа. В случае, если длина не совпадает с типом, по возможности происходит преобразование до нужного типа.

Таб. 5 Длина.

спецификаторы
Длина d, i u o x X f F e E g G a A c s p n
(none) int unsigned int double int char* void* int*
hh signed char unsigned char signed char*
h short int unsigned short int short int*
l long int unsigned long int wint_t wchar_t* long int*
ll long long int unsigned long long int long long int*
j intmax_t uintmax_t intmax_t*
z size_t size_t size_t*
t ptrdiff_t ptrdiff_t ptrdiff_t*
L long double

Форматированный ввод

Рассмотрим форматированный ввод функцией scanf.
int scanf(const char*, . )
Функция принимает строку формата ввода (она похожа на строку формата printf) и адреса, по которым необходимо записать считанные данные. Возвращает количество успешно проинициализированных аргументов.
Формат спецификатора ввода
%[*][ширина][длинна]спецификатор

Таб. 6 Спецификатор типа.

Спецификатор Описание Выбранные символы
i, u Целые Произвольное число цифр (0-9), возможно, начинающихся с + или -. Если число начинается с 0, то считывается в восьмеричном формате, если с 0x, то в шестнадцатеричном.
d Десятичное целое Произвольное число цифр (0-9), возможно, начинающихся с + или -.
o восьмеричное целое Произвольное число цифр (0-7), возможно, начинающихся с + или -.
x Шестнадцатеричное целое Произвольное число цифр (0-F), возможно, начинающихся с + или — и префикса 0x или 0X.
f, e, g Число с плавающей точкой Число, состоящее из набора цифр 0-9, возможно с десятичным разделителем (точкой). Возможно также представление в экспоненциальной форме. C99 позволяет также вводить число в шестнадцатеричном формате.
a
c Символ Если ширина не передана, то считывает один символ. Если ширина передана, то считывает нужное количество символов и размещает их в массиве БЕЗ терминального символа на конце.
s Строка Считывает все не пробельные символы. Если указана ширина, то не более n символов. Ставит на место n+1 символа терминальный.
p Адрес указателя Последовательность символов, трактуемая как адрес указателя. Формат зависит от реализации, но совпадает с тем, как выводит printf с ключом p
[символы] Множество символов Считывает только те символы, которые записаны в квадратных скобках, С99
[^символы] Множество символов Считывает только те символы, которые не указаны в квадратных скобках, С99
n Ничего не считывает Сохраняет число уже считанных символов по указанному адресу

Как и в printf, ширина, заданная символом * ожидает аргумента, который будт задавать ширину. Флаг длина совпадает с таким флагом функции printf.

Кроме функций scanf и printf есть ещё ряд функций, которые позволяют получать вводимые данные

int getch() [aka _getch(), getchar()] — возвращает введённый символ, при этом не выводит его на консоль.

char * fgets ( char * str, int num, FILE * stream ) — функция позволяет считывать строку с пробельными символами. Несмотря на то, что она работает с файлом, можно с её помощью считывать и из стандартного потока ввода. Её преимущество относительно gets в том, что она позволяет указать максимальный размер считываемой строки и заканчивает строку терминальным символом.

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

Непечатные символы

В си определён ряд символов, которые не выводятся на печать, но позволяют производить форматирование вывода. Эти символы можно задавать в виде численных значений, либо в виде эскейп-последовательностей: символа, экранированного обратным слешем.

Источник

4.8 – Числовые типы с плавающей точкой

Целочисленные типы отлично подходят для подсчета целых чисел, но иногда нам нужно хранить очень большие числа или числа с дробной частью. Переменная типа с плавающей точкой (запятой) – это переменная, которая может содержать действительное число, например 4320,0, -3,33 или 0,01226. «Плавающая» в названии «с плавающей точкой» указывает на то, что десятичная точка может «плавать»; то есть она может поддерживать переменное количество цифр до и после себя.

Существует три разных типа данных с плавающей точкой: float , double и long double . Как и в случае с целыми числами, C++ не определяет фактические размеры этих типов (но гарантирует минимальные размеры). В современных архитектурах представление с плавающей точкой почти всегда соответствует двоичному формату IEEE 754. В этом формате float составляет 4 байта, double – 8, а long double может быть эквивалентно double (8 байтов), 80 битам (часто с дополнением до 12 байтов) или 16 байтам.

Типы данных с плавающей запятой всегда идут со знаком (могут содержать положительные и отрицательные значения).

Категория Тип Минимальный размер Типовой размер
С плавающей запятой float 4 байта 4 байта
double 8 байт 8 байт
long double 8 байт 8, 12 или 16 байт

Ниже показан пример определения чисел с плавающей запятой:

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

Обратите внимание, что по умолчанию литералы с плавающей точкой по умолчанию имеют тип double . Суффикс f используется для обозначения литерала типа float .

Лучшая практика

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

Предупреждение

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

Печать чисел с плавающей точкой

Теперь рассмотрим следующую простую программу:

Результаты работы этой, казалось бы, простой программы могут вас удивить:

В первом случае std::cout напечатал 5, хотя мы ввели 5.0. По умолчанию std::cout не будет печатать дробную часть числа, если она равна 0.

Во втором случае число печатается так, как мы и ожидали.

В третьем случае напечаталось число в экспоненциальном представлении (если вам нужно освежить в памяти экспоненциальное представление, смотрите урок «4.7 – Введение в экспоненциальную запись»).

Диапазоны значений типов с плавающей точкой

Предполагая, что используется представление IEEE 754:

Размер Диапазон значений Точность
4 байта от ±1,18 × 10 -38 до ±3,4 × 10 38 6-9 значащих цифр, обычно 7
8 байт от ±2,23 × 10 -308 до ±1,80 × 10 308 15-18 значащих цифр, обычно 16
80 бит (обычно используется 12 или 16 байт) от ±3,36 × 10 -4932 до ± 1,18 × 10 4932 18-21 значащая цифра
16 байт от ±3,36 × 10 -4932 до ± 1,18 × 10 4932 33-36 значащих цифр

80-битный тип с плавающей запятой – это своего рода историческая аномалия. На современных процессорах он обычно реализуется с использованием 12 или 16 байтов (что является более естественным размером для обработки процессорами).

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

Точность с типов плавающей запятой

Рассмотрим дробь 1/3. Десятичное представление этого числа – 0,33333333333333… с тройками, уходящими в бесконечность. Если бы вы писали это число на листе бумаги, ваша рука в какой-то момент устала бы, и вы, в конце концов, прекратили бы писать. И число, которое у вас осталось, будет близко к 0,3333333333…. (где 3-ки уходят в бесконечность), но не совсем.

На компьютере число бесконечной длины потребует для хранения бесконечной памяти, но обычно у нас есть только 4 или 8 байтов. Эта ограниченная память означает, что числа с плавающей запятой могут хранить только определенное количество значащих цифр – и что любые дополнительные значащие цифры теряются. Фактически сохраненное число будет близко к необходимому, но не точно.

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

При выводе чисел с плавающей точкой std::cout по умолчанию имеет точность 6, то есть предполагает, что все переменные с плавающей точкой имеют только до 6 значащих цифр (минимальная точность с плавающей точкой), и, следовательно, он будет отсекать всё, что идет дальше.

Следующая программа показывает усечение std::cout до 6 цифр:

Эта программа выводит:

Обратите внимание, что каждое из напечатанных значений имеет только 6 значащих цифр.

Также обратите внимание, что std::cout в некоторых случаях переключился на вывод чисел в экспоненциальном представлении. В зависимости от компилятора показатель степени обычно дополняется до минимального количества цифр. Не беспокойтесь, 9.87654e+006 – это то же самое, что 9.87654e6 , только с некоторым количеством дополнительных нулей. Минимальное количество отображаемых цифр показателя степени зависит от компилятора (Visual Studio использует 3, некоторые другие в соответствии со стандартом C99 используют 2).

Число цифр точности переменной с плавающей запятой зависит как от размера (у float точность меньше, чем у double ), так и от конкретного сохраняемого значения (некоторые значения имеют большую точность, чем другие). Значения float имеют точность от 6 до 9 цифр, при этом большинство значений float имеют не менее 7 значащих цифр. Значения double имеют от 15 до 18 цифр точности, при этом большинство значений double имеют не менее 16 значащих цифр. Значения long double имеет минимальную точность 15, 18 или 33 значащих цифр в зависимости от того, сколько байтов этот тип занимает.

Мы можем переопределить точность по умолчанию, которую показывает std::cout , используя функцию манипулятора вывода с именем std::setprecision() . Манипуляторы вывода изменяют способ вывода данных и определяются в заголовке iomanip .

Поскольку с помощью std::setprecision() мы устанавливаем точность в 16 цифр, каждое из приведенных выше чисел печатается с 16 цифрами. Но, как видите, числа определенно неточны до 16 цифр! А поскольку числа float менее точны, чем числа double , число ошибок у float больше.

Проблемы с точностью влияют не только на дробные числа, они влияют на любое число со слишком большим количеством значащих цифр. Рассмотрим большое число:

123456792 больше, чем 123456789 . Значение 123456789.0 имеет 10 значащих цифр, но значения float обычно имеют точность 7 цифр (и результат 123456792 точен только до 7 значащих цифр). Мы потеряли точность! Когда теряется точность из-за того, что число не может быть точно сохранено, это называется ошибкой округления.

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

Лучшая практика

Если нет ограничений по использованию памяти, отдавайте предпочтение использованию double вместо float , поскольку неточность float часто приводит к погрешностям.

Ошибки округления затрудняют сравнение чисел с плавающей запятой

С числами с плавающей запятой сложно работать из-за неочевидных различий между двоичными (как хранятся данные) и десятичными (как мы думаем) числами. Рассмотрим дробь 1/10. В десятичном формате ее легко представить как 0,1, и мы привыкли думать о 0,1 как о легко представимом числе с 1 значащей цифрой. Однако в двоичном формате 0,1 представлен бесконечной последовательностью: 0,00011001100110011… Из-за этого, когда мы присваиваем 0,1 числу с плавающей точкой, мы сталкиваемся с проблемами точности.

Эффект от этого можно увидеть в следующей программе:

Эта программ выводит следующее:

Как и ожидалось, в первой строке std::cout печатает 0,1 .

Во второй строке, где std::cout показывает нам 17-значную точность, мы видим, что d на самом деле не совсем равно 0,1 ! Это связано с тем, что из-за ограниченной памяти double пришлось усекать приближение. В результате получается число с точностью до 16 значащих цифр (что гарантирует тип double ), но это число не равно 0,1. Ошибки округления могут сделать число немного меньше или немного больше, в зависимости от того, где происходит усечение.

Ошибки округления также могут иметь неожиданные последствия:

Хотя можно было ожидать, что d1 и d2 должны быть равны, мы видим, что это не так. Если бы мы сравнивали d1 и d2 в программе, программа, вероятно, не работала бы так, как ожидалось. Поскольку числа с плавающей запятой имеют тенденцию быть неточными, их сравнение обычно проблематично – мы обсудим эту тему (и решения) подробнее в уроке «5.6 – Операторы отношения и сравнение значений с плавающей запятой».

Последнее замечание об ошибках округления: математические операции (такие как сложение и умножение), как правило, приводят к увеличению ошибок округления. Таким образом, даже несмотря на то, что 0,1 имеет ошибку округления в 17-й значащей цифре, когда мы складываем 0,1 десять раз, ошибка округления добралась бы и до 16-й значащей цифры. Продолжение операций приведет к тому, что эта ошибка станет всё более значительной.

Ключевые выводы

Ошибки округления возникают, когда число не может быть сохранено точно. Это может произойти даже с простыми числами, например, 0,1 . Таким образом, ошибки округления могут происходить постоянно. Ошибки округления – не исключение, а правило. Никогда не предполагайте, что ваши числа с плавающей запятой точны.

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

NaN и Inf

Существует две особые категории чисел с плавающей запятой. Первая – Inf , которая представляет бесконечность. Inf может быть положительной или отрицательной. Вторая – NaN , что означает «Not a Number» (не число). Существует несколько различных типов NaN (которые мы здесь обсуждать не будем). NaN и Inf доступны только в том случае, если компилятор для чисел с плавающей запятой использует определенный формат (IEEE 754). Если используется другой формат, следующий код приводит к неопределенному поведению.

Ниже приведена программа, показывающая все эти три категории чисел с плавающей точкой:

И результаты работы этой программы при использовании Visual Studio 2008 в Windows:

INF означает бесконечность, а IND означает неопределенность. Обратите внимание, что результаты печати Inf и NaN зависят от платформы, поэтому ваши результаты могут отличаться.

Лучшая практика

Вообще избегайте деления на 0, даже если ваш компилятор поддерживает это.

Заключение

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

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

Источник

Читайте также:  Чем отстирать белую рубашку от краски акварель
Оцените статью