Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык C. Ч3.docx
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
254.2 Кб
Скачать

Int main(void)

{

float a, b; b = 2.0e20 + 1.0;

a = b - 2 . 0e20 ;

printf("%f \n", a);

return 0;

}

Вывод выглядит следующим образом:

0.000000 <- старая версия компилятора дсс в операционной системе Linux

-13584010575872.000000 <-Turbo С 1.5

400817546854 4.000000 <-XCode 4.5, Visual Studio 2012, текущая версия компилятора gсс.

Причина получения таких странных результатов состоит в том, что компьютер не следит за тем, чтобы под числа с плавающей запятой было отведено столько десятичных позиций, сколько нужно для правильного выполнения операции. Число 2.0е20 представлено цифрой 2, за которой следует 20 нулей, и за счет прибавления 1 вы пытаетесь изменить 21-ю цифру. Чтобы эта операция выполнилась корректно, программа должна иметь возможность хранить число, состоящее из 21 цифры. Число типа float — это обычно шесть или семь цифр, мас­штабированных при помощи показателя степени до большего или меньшего числа, так что такая попытка сложения обречена на неудачу. С другой стороны, если вместо 2.0е20 вы ука­жете 2.0е4, то получите правильный ответ, поскольку вы пытаетесь изменить пятую цифру, а числа типа float обладают достаточной для этой операции точностью.

Представление значений с плавающей запятой

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

Какие размеры типов используются в вашей системе? Чтобы выяснить это, попро­буйте выполнить программу, показанную в листинге 3.8.

Листинг 3.8. Программа typesize.c

/* typesize.c -- prints out type sizes */

#include <stdio.h>

Int main(void)

{

/* c99 provides a %zd specifier for sizes */

printf("Type int has a size of %zd bytes.\n", sizeof(int));

printf("Type char has a size of %zd bytes.\n", sizeof(char));

printf("Type long has a size of %zd bytes.\n", sizeof(long));

printf("Type long long has a size of %zd bytes.\n",

sizeof(long long));

printf("Type double has a size of %zd bytes.\n",

sizeof(double));

printf("Type long double has a size of %zd bytes.\n",

sizeof(long double));

return 0;

}

В языке С имеется встроенная операция sizeof, которая возвращает размер типа в байтах. Для такого применения sizeof в стандартах С99 и Cl 1 предоставляется спецификатор %zd. Компиляторы, несовместимые с этими стандартами, могут потребо­вать вместо него спецификатор %и или %1и. Ниже показан пример вывода программы

typesize.c:

Тип int имеет размер 4 байт(ов).

Тип char имеет размер 1 байт(ов) .

Тип long имеет размер 8 байт(ов) .

Тип long long имеет размер 8 байт(ов) .

Тип double имеет размер 8 байт(ов).

Тип long double имеет размер 16 байт(ов).

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

Использование типов данных

При разработке программы обращайте внимание на то, какие переменные необхо­димы, и какие типы они должны иметь. Скорее всего, для чисел вы выберете int или, возможно, float, а для символов — тип char. Объявляйте переменные в начале фун­кции, в которой они используются. Выбирайте для переменных имена, отражающие их предназначение. При инициализации обеспечьте соответствие типов констант и типов переменных. Например:

int apples =3; /* правильно */

int oranges = 3.0; /* плохая форма */

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

Когда вы инициализируете переменную одного числового типа значением друго­го числового типа, компилятор С преобразует такое значение в тип переменной. Это означает возможность потери данных. Например, взгляните на следующие примеры инициализации:

int cost = 12.99; /* инициализация переменной типа int значением double */

float pi = 3.1415926536; /* инициализация переменной типа float значением double */

В первом объявлении переменной cost присваивается значение 12; при преобразовании значений с плавающей запятой в целочисленные компилятор С вместо округления просто отбрасывает дробную часть числа (выполняет усечение). Во втором объявлении происходит некоторая потеря точности, поскольку для типа float точ­ность гарантируется только в пределах шести цифр. Когда вы делаете такую инициа­лизацию, компиляторы могут (но не обязаны) выдавать предупреждающее сообщение.

Управляющие последовательности

Давайте рассмотрим еще один пример, связанный с выводом, в котором используются специальные управляющие последовательности для символов языка С. В частности, программа, представленная в листинге 3.10, демонстрирует работу символов возврата на одну позицию влево (\Ь), табуляции (\t) и возврата каретки (\г). Их концепции существуют со времен, когда компьютеры применяли для вывода телетайпы, и они не всегда успешно транслируются в современных графических интерфейсах. Например, код в листинге 3.10 не работает описанным здесь образом в некоторых реализациях для компьютеров Macintosh.

Листинг 3.10. Программа escape.с

/* escape.c -- uses escape characters */

#include <stdio.h>