Основным назначением любой прикладной программы является преобразование исходных данных в соответствии с заданным алгоритмом, а большая часть исходных данных обычно представлена числовой информацией. В этом разделе мы познакомимся с формой записи числовых данных в программах на языках C, C++ и набором операций, которые можно использовать для различных типов числовых данных. Термин системные, использованный в заголовке раздела, означает, что данные этого типа являются "родными" для системы программирования. Их не надо описывать, не надо определять, как выполняются арифметические или какие- либо другие операции над данными этих типов. Иногда такие данные называют базовыми. Как правило, системные типы данных в алгоритмических языках повторяют те числовые форматы, которые предусмотрены системой машинных команд ПК.
Числовые данные условно можно разбить на три категории - положительные целочисленные данные (их значения в компьютере представлены целыми двоичными числами без знака), произвольные целочисленные данные (один из двоичных разрядов их представления играет роль знакового разряда) и числовые данные вещественного типа.
Для хранения целочисленных данных со знаком в IBM PC используется дополнительный двоичный код. Эта особенность распространяется только на отрицательные числа. Для получения дополнительного кода отрицательного числа нужно перевернуть все двоичные разряды соответствующего положительного числа и прибавить единицу в старший разряд. Например:

Дополнительный код позволяет примерно на 25% ускорить выполнение таких операций как сложение и вычитание.
Самые короткие целочисленные данные со знаком представлены в памяти
IBM-совместимых ПК одним байтом, в котором может разместиться любое
число из диапазона от -128 до 127, записанное в дополнительном коде. В
языках C, C++ для описания переменных такого типа используется
спецификатор char. В одном же байте может быть расположено и самое
короткое целое число без знака. По терминологии C таким числам
соответствует спецификатор unsigned char. Диапазон допустимых данных
при этом смещается вправо и равен [0, 255].
Вторая категория целочисленных данных в системах программирования,
эксплуатируемых на IBM PC, представлена двухбайтовыми целыми числами.
В варианте со знаком они поддерживают диапазон от -32768 до +32767, в
варианте без знака - от 0 до 65535.
Язык системы BC 3.1 использует для описания двухбайтовых
целочисленных данных спецификаторы int (короткое целое со знаком) и unsigned int (короткое целое без знака). При их использовании
арифметические операции над короткими операндами выполняются корректно
при условии, что результат не выходит за пределы разрешенного
диапазона. Однако если к максимальному целому числу прибавить 1, то
вместо положительного числа +32768 в компьютере окажется отрицательное
число -32768. И никакого предупреждения о нарушении допустимого
интервала система не выдаст. Считается, что программист сам должен
позаботиться о соответствующих проверках. В версии Borland C++ Builder
для объявления двухбайтовых целочисленных данных используются
спецификаторы short и unsigned short.
Третья категория целых чисел в IBM PC представлена четырехбайтовыми
данными. В варианте со знаком они перекрывают диапазон от -2147483648
до +2147483647, в варианте без знака - от 0 до 4294967295.
Для описания четырехбайтовых данных целого типа в языках C, C++
используются спецификаторы long (эквивалент long int ) и unsigned long.
В среде визуального программирования C++ Builder спецификаторы int и long эквивалентны.
Несмотря на то, что микропроцессоры IBM PC уже давно поддерживают
восьмибайтовый целочисленный формат, обеспечивающий диапазон от -263
до 263-1, системы программирования довольно долго обходили этот формат
или использовали его особым образом. Так, например, системы Turbo
Pascal на базе этого формата предложили тип данных comp, который был
причислен к разряду данных вещественного типа. В современных
визуальных средах этот тип данных в своем естественном виде
представляет числовые объекты типа int64. В недалеком будущем системы
программирования воспользуются и сверхдлинными целочисленными данными
типа int128.
Для внутреннего представления данных вещественного типа характерно
то, что в соответствующей области оперативной памяти хранятся две
компоненты числа - мантисса m и порядок p. Само число x при этом равно
произведению m*2p. Таким образом, мантисса определяет значащие цифры
числа и его знак, а порядок - положение запятой, которая благодаря
этому как бы "плавает" между значащими цифрами (отсюда и термин - формат с плавающей запятой ). Такой способ представления числовых
данных позволяет при одинаковом количестве двоичных разрядов,
отведенных для хранения чисел существенно расширить диапазон
допустимых данных.
Попробуем оценить тот выигрыш в диапазоне допустимых чисел, который
обеспечивает формат вещественных данных по сравнению с целочисленным
форматом. Рассмотрим 32-битные двоичные числа. Целочисленные значения
со знаком в этом формате позволяют работать с числами, принадлежащими
по модулю интервалу [0, 2*109]. Предположим, что для числа с плавающей
запятой в 32-битном слове отведены 1 двоичный разряд под знак числа, 8
двоичных разрядов под порядок и оставшиеся 23 разряда - под 2-1 ) на минимальный порядок ( 2-128 ), т.е. 2-129, что
примерно соответствует 10-39. Самое большое по модулю число
представляет произведение (1-2-23)*2+127, что примерно соответствует 10+38. Таким образом, если целые числа перекрывали диапазон в 9
десятичных порядков, то формат с плавающей запятой при той же
разрядности слова перекрывает диапазон в 77 порядков. Однако нельзя не
заметить и проигрыш в количестве значащих цифр. Целочисленный 32-
битный формат поддерживает 10 значащих цифр, тогда как 23-битные
Наиболее часто применяемые типы вещественных чисел представлены короткими (4 байта) и длинными (8 байт) данными. Короткий вещественный формат по модулю обеспечивает представление чисел в диапазоне приблизительно от 10-38 до 10+38 с 7-8 значащими десятичными цифрами. Для 8-байтового формата диапазон существенно расширяется - от 10-308 до 10+308, а количество значащих цифр увеличивается до 15-16. Сопроцессор IBM PC предлагает расширенный десятибайтовый формат вещественных данных, перекрывающий диапазон (по модулю) от 10-4932 до 10+4932 и сохраняющий 19-20 значащих цифр.
В системах программирования Borland C++ для float (короткое
вещественное, 4 байта), double (вещественное с удвоенной точностью, 8
байт) и long double (длинное вещественное с удвоенной точностью, 10
байт).
В машинном представлении вещественных данных разного типа на IBM PC
не выдержана какая-то общая идеология. Объясняется это, по всей
вероятности, разными наслоениями на прежние аппаратные решения,
которые принимались при разработке процессоров в разных отделениях
фирмы Intel. Поэтому здесь имеют место такие нюансы, как сохранение в
оперативной памяти или не сохранение старшего бита 0.5 <= m < 1 ) или
смешанного ( 1 <= m < 2 ) числа и т.п. Прикладных программистов эти
детали мало интересуют, однако при создании специальных системных
компонент с точным представлением данных приходится считаться.
В программах на языках C, C++ встречаются литеральные и именованные числовые константы целого или вещественного типа. Числовые константы, употребляемые в тексте программ в арифметических или логических выражениях, называют литеральными. Они представлены числовыми литерами - цифрами, знаками + или -, точками, отделяющими целую часть числа от дробной, показателями десятичного порядка. Например:
x=-25; //целочисленная константа -25 y=y+2.4; //вещественная константа в форме с фиксированной запятой z=2.1e-6; //вещественная константа в форме с плавающей запятой
В отличие от литеральных констант программисты часто прибегают к константам, которые подобно переменным имеют индивидуальные имена:
#define Nmax 100
const double eps=1e-6;
..................
int a[Nmax]
..................
for(i=0; i<Nmax; i++)
....................
if(abs(z)<eps)...
Удобство
Под внешним представлением числовой информации мы подразумеваем способы записи данных, используемые в текстах программ, при наборе чисел, вводимых в ЭВМ по запросу программы, при отображении результатов на экране дисплея или на принтере.
Наличие в естественной записи числа точки ( 3.1415 ) или указателя
десятичного порядка ( 314.159265e-02, 314.159265E-02 ) означает, что
соответствующее значение представлено в ЭВМ в виде вещественного числа
с плавающей запятой.
Кроме естественного представления числовых констант в виде целого или вещественного числа языки программирования допускают различные добавки в начале (" префиксы ") или конце (" суффиксы ") числа, определяющие способы преобразования и хранения данных в памяти компьютера.
В алгоритмическом языке C активно используются как префиксы, так и суффиксы:
0x5,0X5 - шестнадцатеричное целое число (префикс - 0x или 0X );05 - восьмеричное целое число (префикс - незначащий нуль в начале);5H,5h - короткое целое число (суффикс - h или H от sHort )5U,5u - целое число без знака (суффикс - u или U, от Unsigned );5HU,5hu,5Hu,5hU - короткое целое число без знака;5L,5l - длинное целое число (суффикс - l или L, от Long );5LU,5lu,5Lu,5lU - длинное целое число без знака;5f,5F - короткое вещественное число (суффикс - f или F, от Float );5LF,5FL,5fl,5lf,5Lf,5lF,5Fl,5fL - длинное вещественное число.Входной язык системы визуального программирования BCB дополнительно позволяет использовать следующие суффиксы:
i8 - для однобайтовых целых чисел со знаком (например, 127i8 )i16 - для двухбайтовых целых чисел со знаком;i32 - для четырехбайтовых целых чисел со знаком;i64 - для восьмибайтовых целых чисел со знаком;ui64 - для восьмибайтовых целых чисел без знака.Переменные числового типа, используемые в программе, обязательно должны быть объявлены до их использования в тех или иных исполняемых операторах. В отличие от языка Pascal алгоритмические языки C, C++ позволяют вводить такие описания не только в начале программных единиц (функций), но и по ходу формирования программы:
int main(void)
{ int i,j;
...........
s=0;
for(int k=0; k<10; k++)
........................
В приведенном примере переменные i и j объявлены в начале функции,
а объявление переменной k встретилось в операторе цикла. По стандарту
языка C++ место объявления переменной определяет сферу ее действия.
Переменные, описанные в начале функции (такие как i и j в приведенном
выше примере), считаются локализованными в данной функции и могут быть
использованы в любой части тела этой функции. Повторное объявление
переменных с такими же именами считается ошибкой (дублирование имен
переменных). В отличие от этого действие переменных, объявленных в
некотором внутреннем блоке функции (в нашем примере переменная k
объявлены в цикле for ), распространяется только на время работы этого
блока. Т.е после выхода из цикла память, выделенная под переменную k,
возвращается системе, и без повторного переобъявления этой переменной
пользоваться уже нельзя. Следует отметить, что это положение стандарта
языка C++ в системе BC 3.1 реализовано не так. Повторное объявление
такой переменной в следующем внутреннем блоке в системе BC 3.1
приводит к сообщению об ошибке, т.к. при выходе из цикла переменная k
продолжает свою жизнь. В средах визуального программирования Borland
C++ Builder этот пассаж исправлен.
Объявлению любой переменной предшествует служебное слово, определяющее диапазон допустимых значений - тип переменной. Имена базовых типов числовых данных и соответствующие им характеристики, приведены в табл. 3.1 (BC 3.1) и 3.2 (BCB). Для вещественных чисел указаны минимальные по модулю значения.
| Тип | Длина | Минимальное значение | Максимальное значение |
|---|---|---|---|
char, signed char |
1 байт | -128 |
127 |
unsigned char |
1 байт | 0 |
255 |
short int, short |
2 байта | -32768 |
32767 |
unsigned short |
2 байта | 0 |
65535 |
int, signed |
2 байта | -32768 |
32767 |
unsigned int, unsigned |
2 байта | 0 |
65535 |
long, long int |
4 байта | -2147483648 |
2147483647 |
unsigned long |
4 байта | 0 |
4294967265 |
float |
4 байта | 3.4*e-38 |
3.4*e38 |
double |
8 байт | 1.7e-308 |
1.7e308 |
long double |
10 байт | 3.4e-4932 |
1.1e4932 |
| Тип | Длина | Минимальное значение | Максимальное значение |
|---|---|---|---|
char, __int8 |
1 байт | -128 |
127 |
unsigned char |
1 байт | 0 |
255 |
short int, short, __int16 |
2 байта | -32768 |
32767 |
unsigned short |
2 байта | 0 |
65535 |
int, signed, __int32 |
4 байта | -2147483648 |
2147483647 |
unsigned int, unsigned |
4 байта | 0 |
4294967265 |
long, long int |
4 байта | -2147483648 |
2147483647 |
unsigned long |
4 байта | 0 |
4294967265 |
int64, __int64 |
8 байт | -4611686018427387904 |
4611686018427387903 |
__uint64 |
8 байт | 0 |
9223372036854775807 |
float |
4 байта | 3.4*e-38 |
3.4*e38 |
double |
8 байт | 1.7e-308 |
1.7e308 |
long double |
10 байт | 3.4e-4932 |
1.1e4932 |
Объявление переменной можно совместить с присвоением ей начального значения ( инициализацией ):
int x=18,y=-5; float a=5F;
В отличие от языка Pascal инициализация локальных переменных в функциях на языках C, C++ происходит при каждом вызове функции.
Для начинающих программистов проще всего организовать в программе
ввод числовой информации из буфера потока, связанного с так называемым
стандартным устройством ввода ( stdin ). Данные в этом буфере
оказываются в тот момент, когда программа обращается к пользователю и
ждет окончания набора затребованных числовых значений с клавиатуры.
Программа может запросить одно или несколько значений:
cin >> d; ........... cin >> x1 >> x2 >> x3;
Первая строка соответствует запросу на ввод значения единственной
переменной d. Следующая строка программы представляет запрос на ввод
трех числовых значений, первое из которых будет присвоено переменной x1, второе значение предназначается для переменной x2, третье значение
- для переменной x3. В ответ на запрос программы пользователь должен
набрать на клавиатуре затребованные числа, разделяя их, по крайней
мере, одним пробелом. Набор строки завершается нажатием клавиши Enter.
Количество числовых значений, набираемых пользователем в пределах
строки, может оказаться как меньше, так и больше, чем требуется
программе. В первом случае продолжение программы задержится до тех
пор, пока пользователь не введет дополнительные строки с недостающими
значениями. Если пользователь наберет слишком много значений в
вводимой строке, то лишние числовые данные сохранятся в буфере ввода и
будут переданы программе при выполнении следующего оператора cin.
Тип набираемого числового значения и его величина должны соответствовать типу переменной, в которую это значение должно быть введено. Забота об этом целиком возлагается на пользователя.
Для обеспечения потокового ввода к программе следует подключить
заголовочный файл iostream.h:
#include <iostream.h>
int main()
{ int i;
float f;
double d;
..........
cin >> i >> f >> d;
В переменные типа char или unsigned char из потока числовые
значения ввести невозможно, т.к. они в данном случае воспринимаются
системой не как числовые данные, а как символьные. В такие переменные
попадет только первый символ набранного значения.
Гораздо более сложным для начинающих программистов является ввод
числовых данных, организуемый с помощью функции scanf, использующей
так называемую форматную строку:
#include <stdio.h>
int main()
{ int i;
float f;
double d;
..........
scanf("%d %f %lf",i,f,d);
Строка вводимых данных поступает со стандартного устройства ввода
( stdin ), которым по умолчанию считается клавиатура. Завершение набора
строки ввода - нажатие клавиши Enter.
Первый аргумент функции scanf представляет форматную строку,
В приведенном примере переменной i (в списке ввода указан ее адрес
- i ), объявленной с помощью спецификатора типа int, соответствует
форматный указатель %d. Это означает, что первым числовым значением в
строке ввода может быть только целое десятичное число со знаком ( d -
от decimal, десятичный). Вещественной переменной f типа float в
форматной строке соответствует указатель %f. Это означает, что второе
числовое значение в строке ввода должно принадлежать диапазону,
предусмотренному для коротких вещественных данных. Для переменной d
типа double использован форматный указатель %lf ( l - от long ).
Как правило, количество форматных указателей, перечисленных в
первом аргументе функции scanf, должно совпадать с количеством адресов
переменных, следующих за форматной строкой. Исключение составляет
случай, когда форматный указатель предписывает программе пропустить
очередное значение из введенной строки. В этом случае количество
адресов в списке ввода уменьшается соответствующим образом.
Например:
scanf("%d %*l %lf",i,d);
При выполнении такого оператора ввода программа проигнорирует второе числовое значение, набранное пользователем. Конечно, при ручном наборе вводимых значений, нелепо заставлять пользователя набирать данные, которые программе не понадобятся. Но такая возможность может оказаться полезной, когда строка ввода поступает не с клавиатуры, а из других источников (считана с диска, сформирована другой программой в оперативной памяти).
Вообще говоря, функция scanf возвращает числовое значение, равное
количеству правильно обработанных полей из строки ввода. Это полезно
помнить при организации проверки правильности ввода, т.к. сообщения об
ошибках ввода функция scanf не выдает, но после первой же ошибки
прерывает свою работу.
Основная сложность в овладении тонкостями ввода, управляемого
списком форматных указателей, заключается в многообразии последних. В
самом общем виде числовой форматный указатель, используемый функцией scanf, представляется как следующая последовательность управляющих
символов и дополнительных признаков:
%[*][ширина][{l|h|L}]{d|i|u|o|x|X|f|e|E|g|G}
Квадратные скобки здесь означают, что соответствующий элемент
форматного указателя может отсутствовать. В фигурных скобках указаны
символы, один из которых может быть выбран. Обязательными элементами
любого форматного указателя являются начальный символ % и последний
символ, определяющий тип вводимого значения.
Символ * после начального символа является указанием о пропуске
соответствующего значения из строки ввода. Необязательное, и, как
правило, не используемое при вводе, поле ширина задает количество
символов во вводимом значении. Дополнительные признаки l, h и L
уточняют длину машинного формата соответствующей переменной ( l, L -
long; h - short ). Значение последнего обязательного символа форматного
указателя расшифровано в табл. 3.3.
| Символ формата | Допустимое значение в строке ввода |
|---|---|
d |
целое десятичное число со знаком |
i |
целое число |
u |
целое число без знака |
o |
целое восьмеричное число без знака |
x,X |
целое шестнадцатеричное число без знака |
f |
вещественное число |
e,E |
вещественное число |
g,G |
вещественное число |
char. Дело в том, что минимальная длина
числового значения вводимого с помощью функции scanf - 2 байта. И
значение введенного старшего байта затирает в памяти еще один байт
вслед за переменной типа char. Заметить такую ошибку удается не
каждому, но на работу программы такой ввод может повлиять довольно
серьезно. Поэтому возьмите за правило - в однобайтовые переменные типа char числовую информацию вводить нельзя.
В системе BC 3.1 для ввода значений переменных типа short или int
можно использовать один из следующих форматных указателей - %d, %i,
%u, %o, %x или %X. Ввод целочисленных данных типа long требует одного
из следующих форматных указателей - %ld, %li, %lu, %lo, %lx или %lX.
В системе BCB для двухбайтовых числовых переменных следует
использовать форматные указатели в сочетании с символом h - %hd, %hi,
%hu, %ho, %hx или %hX. Форматные указатели %d и %ld здесь
эквивалентны.
Такое обилие целочисленных форматных указателей объясняется тем,
что при наборе целых чисел можно добавлять префиксы - 0x или 0X для
шестнадцатеричных значений, лидирующий 0 для восьмеричных значений.
При использовании форматного указателя %d вводимые данные не могут
содержать указанные префиксы, все значения интерпретируются только как
целые десятичные числа со знаком. При использовании форматного
указателя %i ( i - от integer ) можно вводить числа в любой нотации, но
обязательно с соответствующим префиксом. Форматные указатели %o, %x
или %X позволяют вводить восьмеричные или шестнадцатеричные числа как
с соответствующими префиксами, так и без них.
Ввод коротких вещественных данных типа float может быть осуществлен
с использованием любого из следующих форматных указателей - %f, %e,
%E, %g или %G. На вводе все они эквивалентны, их специфика проявляется
только при выводе числовых результатов. Для восьмибайтовых данных типа double к каждому из этих указателей добавляется буква l - %lf, %le,
%lE, %lg или %lG. Десятибайтовым числовым данным типа long double
соответствуют форматные указатели %Lf, %Le, %LE, %Lg или %LG.
Форматный вывод числовых результатов на стандартное устройство
вывода ( stdout ), которым по умолчанию является экран дисплея,
осуществляется с помощью функции printf. Например:
#include <stdio.h>
int main()
{ int i;
float f;
double d;
..........
printf("%d %f %lf", i+1, f, f*d);
Ее аргументы напоминают по форме обращение к функции scanf с той
лишь разницей, что
Для преобразования числовых данных из машинного формата используются форматные указатели, общий вид которых содержит расширенный набор признаков:
%[флажки][ширина][.точность][{l|h|L}]{d|i|u|o|x|X|f|e|E|g|G}
Поле ширина определяет количество
Поле флажков может содержать до четырех управляющих символов из
набора [ -,+,пробел,0,# ]. Символ " минус " устанавливает прижим
выводимого значения к левой границе выделенного поля (по умолчанию
действует правый прижим). Символ " плюс " устанавливает обязательный
режим вывода знака числа (даже если оно положительно). Символ " пробел "
устанавливает такой режим, при котором вместо знака " + " выводится
пробел. Символ " нуль " устанавливает режим вывода чисел, при котором
старшие незначащие позиции поля вывода заполняются нулями.
Символ # влияет на 0x или 0X. Для форматов f, e, и E обязательно отображается десятичная
точка после целой части (по умолчанию, числа, у которых дробная часть
равна 0, выводятся без десятичной точки). Для форматов g и G не
удаляются лидирующие нули и всегда выводится десятичная точка.
В табл. 3.4 и 3.5, заимствованных из файла помощи, показаны способы вывода целочисленного значения I=555 и вещественного значения R=5.5 при различных комбинациях флажков.
Необязательное поле точность задает для целочисленных значений
обязательное количество цифр (если число меньше, то перед его старшим
разрядом добавляется необходимое количество нулей). Например, вывод
целочисленного значения -5 по формату %8.4d выделяет в строке вывода
поле из восьми символов, на котором первые три позиции заполнены
пробелами, а следующие пять символами -0005.
Для данных вещественного типа поле точность определяет количество
цифр в дробной части числа. Не забывайте о количестве значащих цифр,
хранение которых обеспечивает тот или иной тип данных. Например, если
переменной типа float присвоить значение 3.14159265, то на выводе по
формату %10.8f мы увидим результат 3.14159274. Двум последним цифрам
результата доверять нельзя, т.к. формат float обеспечивает хранение
7-8 десятичных цифр.
| Флажки | Формат %6d | Формат %6o | Формат %8x |
|---|---|---|---|
%-+#0 |
+555 |
01053 |
0x22b |
%-+# |
+555 |
01053 |
0x22b |
%-+0 |
+555 |
01053 |
22b |
%-+ |
+555 |
01053 |
22b |
%-#0 |
555 |
01053 |
0x22b |
%-# |
555 |
01053 |
0x22b |
%-0 |
555 |
1053 |
22b |
%- |
555 |
1053 |
22b |
%+#0 |
+00555 |
001053 |
0x00022b |
%+# |
+555 |
01053 |
0x22b |
%+0 |
+00555 |
001053 |
0000022b |
%+ |
+555 |
1053 |
22b |
%#0 |
000555 |
001053 |
0x00022b |
%# |
555 |
01053 |
0x22b |
%0 |
000555 |
001053 |
0000022b |
% |
555 |
1053 |
22b |
| Флажки | Формат %10.2e | Формат %10.2f |
|---|---|---|
%-+#0 |
+5.50e+00 |
+5.50 |
%-+# |
+5.50e+00 |
+5.50 |
%-+0 |
+5.50e+00 |
+5.50 |
%-+ |
+5.50e+00 |
+5.50 |
%-#0 |
5.50e+00 |
5.50 |
%-# |
5.50e+00 |
5.50 |
%-0 |
5.50e+00 |
5.50 |
%- |
5.50e+00 |
5.50 |
%+#0 |
+005.50e+00 |
+0000005.50 |
%+# |
+5.50e+00 |
+5.50 |
%+0 |
+005.50e+00 |
+0000005.50 |
%+ |
+5.50e+00 |
+5.50 |
%#0 |
005.50e+00 |
0000005.50 |
%# |
5.50e+00 |
5.50 |
%0 |
005.50e+00 |
0000005.50 |
% |
5.50e+00 |
5.50 |
Разница между форматами %0x и %0X заключается в том, что в первом
случае шестнадцатеричная запись числа формируется из малых букв
[ a,b,c,d,e,f ], а во втором случае - из больших букв [ A,B,C,D,E,F ].
Для вывода однобайтовых целочисленных данных со знаком (типа char )
можно пользоваться одним из следующих форматов - %o, %0x, %0X, %i, %d,
%ho, %hx, %hX, %hi, %hd. Для вывода однобайтовых целых без знака
наряду с перечисленными указателями допустим формат %u. Однако следует
иметь ввиду, что однобайтовые значения расширяются до двухбайтовых,
сохраняя знак числа. Поэтому попытка вывести однобайтовое значение 127
по формату %u приведет к правильному результату. Но если мы по такому
же формату выдадим число -5, то результатом будет число 65531
(дополнение до максимального двухбайтового числа).
Вывод числовых результатов вещественного типа предусматривает две
формы отображения - с %f и %lf ) или с плавающей запятой (форматные указатели %e и %E ). Форматные
указатели %g и %G предлагают системе самой выбрать один из этих
форматов, который окажется более компактным для выводимого значения.
Большая или маленькая буква в форматных указателях с плавающей запятой
приводит к тому, что порядку числа предшествует большая или маленькая
буква " e ".
При форматном выводе числовых результатов бывает удобно сопроводить их какой-либо пояснительной подписью. Обычно текст такого пояснения включают в форматную строку. Все символы, которые не являются принадлежностью форматных указателей, автоматически переносятся в выводную строку. Например, при выполнении следующих операторов:
x1=127; x2=-350;
printf("x1=%d x2=%d",x1,x2);
на экране дисплея появится строка:
x1=127 x2=-350
Если мы хотим расположить выводимую информацию на экране дисплея с
начала следующей строки, то в начале форматных указателей обычно
вставляют управляющий символ " \n ":
printf("\nx1=%d x2=%d",x1,x2);
Такой же управляющий символ может быть включен в середину или в
конец форматной строки. В языках C, C++ с помощью символа " \ " в
форматную строку могут быть включены и другие управляющие символы (так
называемые
| Символ | Назначение | Символ | Назначение |
|---|---|---|---|
\a |
Звуковой сигнал (код 0x07, |
\\ |
Отображение символа \ |
\b |
Забой предыдущего символа (код 0x08, Backspace) | \' |
Отображение символа ' |
\f |
Переход на новую страницу (код 0x0C, FF) | \" |
Отображение символа " |
\n |
Переход на следующую строку (код 0x0A, LF) | \? |
Отображение символа ? |
\r |
Возврат в начало текущей строки (код 0x0D, CR) | \0xxx |
Отображение символа с восьмеричным кодом xxx |
\t |
Горизонтальная табуляция (код 0x09, HT) | \xhh |
Отображение символа с шестнадцатеричным кодом hh |
\v |
Вертикальная табуляция (код 0x0B, VT) | \xHH |
Отображение символа с шестнадцатеричным кодом HH |
В строковых значениях такие символы как апостроф и вопросительный знак могут набираться и без предшествующего обратного слэша.
Для начинающих программистов организация потокового вывода числовой
информации на стандартное устройство вывода ( stdout ) представляется
более простой:
#include <iostream.h>
int main()
{ int i;
float f;
double d;
..........
cout << i+1 << f << f*d;
Здесь на первых порах не надо заботиться о форматах выводимых
результатов. Для каждого типа данных существуют соответствующие
системные соглашения. Переход в начало следующей строки здесь
осуществляется путем включения в \n ', либо аналогичного ему признака конца строки endl:
cout << i+1 << f << f*d << endl;
В список выводимых данных могут быть вкраплены и пояснительные подписи:
cout << "x1="<< x1 << "x2=" << x2 << '\n';
В потоковом выводе тоже имеются средства управления -
установка setw - установка
ширины поля вывода, setprecision - установка количества отображаемых
цифр и т.п.) и более современные обращения к методам класса
( cout.fill(:), cout.width(...) ). Некоторые из установленных режимов
действуют только на вывод следующего значения, другие сохраняют свое
действие до следующей установки. С некоторыми деталями управления
форматами числовых данных в потоковом выводе можно познакомиться на
примере 4 из следующего параграфа.
Пример 1. Вывод однобайтовых числовых значений
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
int main()
{ char ch1 = 69;
char ch2 = -128;
unsigned char uch1 = 70;
unsigned char uch2 = 129;
__int8 i8_1 = 71;
__int8 i8_2 = -127;
printf("\nch1=%d ch2=%d",ch1,ch2);
printf("\nuch1=%u uch2=%u",uch1,uch2);
printf("\ni8_1=%i i8_2=%i\n",i8_1,i8_2);
cout << "ch1=" << ch1 << endl;
cout << "ch1=" << (int)ch1 << endl;
cout << "ch2=" << ch2 << endl;
cout << "ch2=" << (int)ch2 << endl;
cout << "i8_1=" << i8_1 << endl;
cout << "i8_1=" << (int)i8_1 << endl;
cout << "i8_2=" << i8_2 << endl;
cout << "i8_2=" << (int)i8_2 << endl;
getch();
return 0;
}
//=== Результаты работы ===
ch1=69 ch2=-128
uch1=70 uch2=129
i8_1=71 i8_2=-127
ch1=E
ch1=69
ch2=А
ch2=-128
i8_1=G
i8_1=71
i8_2=Б
i8_2=-127
По результатам работы примера 1 видно, что форматный вывод
однобайтовых числовых данных никаких затруднений не вызывает. Однако
попытка вывести эти же значения в потоке без приведения значений
символьных переменных к int ))
разумных результатов не дает. Вместо этого в поток выводятся символы,
коды которых совпадают с однобайтовыми числовыми значениями.
Пример 2. Вывод однобайтовых числовых значений
#include <stdio.h>
#include <conio.h>
int main()
{
char ch1 = -5, ch2 = -127;
unsigned char uch1 = 25, uch2 = 255;
printf("With sign :\n");
printf("-5=%3d(10) -127=%3d(10)",ch1,ch2);
printf("\nOhne sign :\n");
printf("25=%#3o(8) %#3x(16) %3d(10)",uch1,uch1,uch1);
printf("\n255=%#3o(8) %#3x(16) %3d(10)",uch2,uch2,uch2);
getch();
return 0;
}
//=== Результаты работы ===
With sign :
-5= -5(10) -127=-127(10)
Ohne sign :
25=031(8) 0x19(16) 25(10)
255=0377(8) 0xff(16) 255(10)
В примере 2 однобайтовые числовые значения выданы по формату в виде десятичных, восьмеричных и шестнадцатеричных данных (основание системы записано в круглых скобках после самого значения).
Пример 3. Вывод коротких и длинных вещественных данных
#include <stdio.h>
#include <conio.h>
int main()
{
float f1=3.14159265, f2=20000000;
double d1=3.14159265, d2=20E+125;
printf("\nf1=%10.8f f2=%f", f1,f2);
printf("\nf1=%e f2=%e", f1,f2);
printf("\nf1=%g f2=%g", f1,f2);
printf("\nd1=%12.9f d2=%g", d1,d2);
printf("\nd1=%12.9e d2=%G", d1,d2);
getch();
return 0;
}
//=== Результаты работы ===
f1=3.14159274 f2=20000000.000000
f1=3.141593e+00 f2=2.000000e+07
f1=3.14159 f2=2e+07
d1= 3.141592650 d2=2e+126
d1=3.141592650e+00 d2=2E+126
Пример 4. Управление форматом при выводе в поток
#include <iostream.h>
#include <iomanip.h>
#include <conio.h>
int main()
{ int i=15, j=6, k;
float v=1.23456, x=3.149;
cout << hex;
cout << "i=" << i << " j=" << j << " x=" << x << " v=" << v;
cout.fill('*');
cout << "\ni=" << setw(6) << i << " j=" << j << endl;
cout << setprecision(3) << x << " v=" << v << endl;
cout.width(10);
cout << x << ' ' << j << endl;
for(k=0; k<8; k++)
cout << setprecision(k) << v << endl;
getch();
return 0;
}
//=== Результаты работы ===
i=f j=6 x=3.149 v=1.23456
i=*****f j=6
3.15 v=1.23
******3.15 6
1.23456
1
1.2
1.23
1.235
1.2346
1.23456
1.23456
Над целочисленными данными (константами и переменными) в языках C,
C++ можно выполнять обычные арифметические операции - сложение ( x+y ), вычитание ( z-5 ), умножение ( x1*x3 ) и деление ( y/w ). В отличие от языка
Pascal здесь деление целочисленных операндов дает целочисленный
результат. Например, 5/2=2. Для получения остатка от деления в C, C++
используется операция %, например, 5%2=1.
Целочисленные данные можно подвергать
y=x<<3; // сдвиг влево на три двоичные разряда z=y>>5; // сдвиг вправо на пять двоичных разрядов
Операция сдвига вправо работает по-разному для целых чисел со знаком и целых чисел без знака. Внимательно посмотрите на результат работы следующей программы:
#include <stdio.h>
#include <conio.h>
int main()
{
int x=5, y=-5;
unsigned z=0xFFFFFFFB;
printf("x=%x y=%x z=%x",x,y,z);
printf("\nx<<2=%x x>>2=%x",x<<2,x>>2);
printf("\ny<<2=%x y>>2=%x",y<<2,y>>2);
printf("\nz<<2=%x z>>2=%x",z<<2,z>>2);
getch();
return 0;
}
//=== Результат работы ===
x=5 y=fffffffb z=fffffffb
x<<2=20 x>>2=1
y<<2=ffffffec y>>2=fffffffe
z<<2=ffffffec z>>2=3ffffffe
Дело в том, что для чисел со знаком операция сдвига на n разрядов
эквивалентна умножению на 2n при сдвиге влево или делению на 2n при
сдвиге вправо. Поэтому для отрицательного операнда результат сдвига
должен остаться отрицательным. С этой целью при сдвиге вправо
производится размножение знакового разряда.
Над одноименными двоичными разрядами целочисленных операндов могут
выполняться логические операции - логическое сложение (символ операции
' | '), логическое умножение (символ операции ' '), исключающее ИЛИ
(символ операции ' ^ ') и инвертирование (символ операции ' ~ ').
Приведенная ниже программа иллюстрирует выполнение указанных операций
над переменными x=5 (двоичный код 00...0101 ) и y=7 (двоичный код 00...0111 ).
#include <stdio.h>
#include <conio.h>
int main()
{
int x=5, y=7;
printf("x=%x y=%x",x,y);
printf("\nx|y=%x xy=%x",x|y,xy);
printf("\nx^y=%x ~x=%x",x^y,~x);
getch();
return 0;
}
//=== Результат работы ===
x=5 y=7
x|y=7 xy=5
x^y=2 ~x=fffffffa
Определенную помощь при обработке целочисленных данных могут
оказать системные функции математической библиотеки. Прототипы этих
функций описаны в заголовочных файлах math.h и stdlib.h. Мы упомянем
лишь некоторые из них.
Функция abs(x) возвращает модуль своего аргумента. Аргументом
функций atoi(s) и atol(s) является строка, представляющая запись
целого числа. Каждая из этих функций преобразует символьную запись
числа в соответствующий машинный формат (результат atoi имеет тип int,
результат atol - тип long ) и возвращает полученный результат. Довольно
полезное преобразование выполняют функции itoa и ltoa. Первый их
аргумент - числовое значение типа int или long. Вторым аргументом
является строковый массив (или указатель на строку), куда записывается
результат преобразования. А третий аргумент, значение которого
находится в диапазоне от 2 до 36, определяет основание системы
счисления, в которую преобразуется значение первого аргумента.
В ряде математических алгоритмов, использующих вероятностные методы
(random(N) при
каждом повторном обращении к ней выдает очередное случайное число из
диапазона от 0 до N-1. Эти числа имеют равномерное распределение
вероятностей, что можно трактовать следующим образом. Допустим, мы
обратились к функции random(1000) 1000 раз. Можно утверждать, что
среди полученных чисел почти все будут разными. Конечно, на практике
это не совсем так, но вероятность того, что среди полученных чисел
встретится много одинаковых, близка к нулю. Для игровых программ очень
важно, чтобы при обращении к датчику случайных чисел каждый раз
возникала непредсказуемая последовательность. С этой целью можно
обратиться к функции randomize(), которая случайным образом возмущает
начальное
Продемонстрируем использование датчика случайных чисел на примере
программы перемешивания колоды карт. Предположим, что для
идентификации карт использованы целые числа в диапазоне от 0 до 35
(или до 51). Идея алгоритма перемешивания состоит в многократной
k - очередное случайное число). Организуем пятикратное
перемешивание, чтобы убедиться в том, что каждый раз колода
оказывается в новом непредсказуемом состоянии.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void mixer(char *b)
{ int j;
char i,tmp;
randomize();
for(j=0; j<10000; j++)
{ i=random(36);
tmp=b[0]; b[0]=b[i]; b[i]=tmp;
}
}
int main()
{ char j,k,a[36];
printf("\nPlaying-cards before:\n");
for(k=0; k<36; k++)
{ a[k]=k; printf("%4d",a[k]); }
for(j=0; j<5; j++)
{ mixer(a);
printf("\nPlaying-cards after %d:\n",j);
for(k=0; k<36; k++)
printf("%4d",a[k]);
}
getch();
return 0;
}
//=== Результат работы ===
Playing-cards before:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Playing-cards after 0:
9 0 13 32 18 30 14 24 34 28 7 26 35 16 3 27 5 11 6
23 21 8 31 17 29 2 15 22 33 12 19 25 1 4 10 20
Playing-cards after 1:
28 9 16 1 6 19 3 29 10 33 24 15 20 5 32 22 30 26 14
17 8 34 25 11 12 13 27 31 4 35 23 2 0 18 7 21
Playing-cards after 2:
33 28 5 0 14 23 32 12 7 4 29 27 21 30 1 31 19 15 3
11 34 10 2 26 35 16 22 25 18 20 17 13 9 6 24 8
Playing-cards after 3:
4 33 30 9 3 17 1 35 24 18 12 22 8 19 0 25 23 27 32
26 10 7 13 15 20 5 31 2 6 21 11 16 28 14 29 34
Playing-cards after 4:
18 4 19 28 32 11 0 20 29 6 35 31 34 23 9 2 17 22 1
15 7 24 16 27 21 30 25 13 14 8 26 5 33 3 12 10
При вычислениях с вещественными данными разрешается использовать только четыре арифметические операции - сложение (+), вычитание (-), умножение (*) и деление (/). В арифметическом выражении могут встречаться операнды разных типов, поэтому важно знать, как компилятор определяет тип результирующего значения. Правило это довольно простое, но оно содержит подводные камни. Арифметические действия выполняются в соответствии общепринятыми правилами:
Несмотря на простоту и естественность описанных выше правил,
результаты вычисления некоторых выражений могут поставить в тупик не
очень внимательного программиста. Например, не вызывает сомнения, что
тип выражения 5/2+1.0 должен быть вещественным. Однако к вещественным
данным относятся и значения типа float, и значения типа double, и
значения типа long double. В данном случае, компилятор ориентируется
на тип числовой константы 1.0, которая по правилам системы
программирования преобразуется в машинный формат длинного
вещественного числа (т.е. имеет тип double ). А вот результат
вычисления данного выражения равен 3.000000, и это может вызвать
недоумение. На самом деле, тип каждого слагаемого формулы определяется
независимо от типов других слагаемых. Первое слагаемое представлено
частным от деления двух целых констант, поэтому его тип тоже целый,
т.е. результат деления равен 2, а не 2.5. Затем значения всех
слагаемых приводятся к типу double, и итоговый результат равен 3.0.
Существенную помощь в вычислениях с вещественными данными оказывают
многочисленные математические функции из раздела math.h. Список
некоторых из них приведен в табл. 3.7.
| Возвращаемое значение | |
|---|---|
double |
arccos x |
double asin(double x) |
arcsin x |
double atan(double x) |
arctg x |
double atan2(double x, double y) |
arctg (y/x) |
double atof(const char *s) |
машинный формат числа из строки s |
double |
округление "сверху" |
double cos(double x) |
cos x |
double cosh(double x) |
ch x |
double exp(double x) |
ex |
double |
|x| |
double |
округление "снизу" |
double log(double x) |
ln x |
double log10(double x) |
lg x |
max(a,b) |
максимум (a,b), тип совпадает с типом максимального аргумента |
min(a,b) |
минимум (a,b) , тип совпадает с типом минимального аргумента |
double pow(double x, double y) |
xy |
double pow10(int p) |
10p |
double sin(double x) |
sin x |
double sinh(double x) |
sh x |
double sqrt(double x) |
квадратный корень из x |
double tan(double x) |
tg x |
double tanh(double x) |
th x |
double hypot((double x, double y) |
квадратный корень из x2+y2 |
double |
значение полинома |
double ldexp(double x, int n) |
x*2n |
max и min, которые, на самом деле представлены не
настоящими функциями, а соответствующими stdlib.h.
Для большинства функций типа double с аргументом типа double
имеются их аналоги с данными типа long double. Названия этих функций
отличаются от приведенных в табл. 3.7 добавкой окончания l - (.:
Особо следует остановиться на функциях округления и .
Первая из них возвращает наименьшее целое значение, которое больше или
равно x. Вторая возвращает наибольшее целое значение, не превосходящее x. Обратите внимание на то, что значение, возвращаемое обеими
функциями, представлено в формате double.
ceil(0.1) =1.0 floor(0.1) = 0.0 ceil(0.5) =1.0 floor(0.5) = 0.0 ceil(0.9) =1.0 floor(0.9) = 0.0 ceil(-0.9)=0.0 floor(-0.9)=-1.0 ceil(-0.5)=0.0 floor(-0.5)=-1.0 ceil(-0.1)=0.0 floor(-0.1)=-1.0
Довольно часто программисты используют для округления функцию . Однако иногда она выдает результат, не совпадающий с
общепринятым в математике, например . Конечно, жаль,
что в языках C, C++ нет прямого аналога функции round из Паскаля, но
построить такую функцию совсем не сложно:
int round(double x)
{ int res;
res=(x<0)? x-0.5 : x+0.5;
return res;
}
Если нужно произвести округление в том или ином знаке, то число
можно предварительно разделить или умножить на 10k, округлить, а затем
результат округления умножить или разделить на 10k.
Замечание 1. При выводе числовых результатов вещественного типа необходимые округления производят системные программы вывода.
Замечание 2. Если целочисленной переменной присваивается вещественное значение, то округление не производится. Дробная часть, какой бы она ни была, просто отбрасывается.
В заголовочном файле math.h приводятся определения именованных
констант, которыми полезно воспользоваться в своих программах (см.
табл. 3.8).
| Константа | Значение | Константа | Значение |
|---|---|---|---|
M_PI |
$$\pi$$ | M_E |
e=2.718... |
M_PI_2 |
$$\pi /2$$ | M_LOG2E |
log2e |
M_PI_4 |
$$\pi /4$$ | M_LOG10E |
log e |
M_2_PI |
$$2/\pi$$ | M_LN2 |
ln 2 |
M_1_SQRTPI |
$$1/\sqrt \pi$$ | M_LN10 |
ln 10 |
M_2_SQRTPI |
$$2/ \sqrt \pi$$ | M_SQRT2 |
$$\sqrt 2=1.414..$$. |
M_SQRT_2 |
$$\sqrt 2/2$$ |
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.