Базовые типы
Целый (integer). Представляет множество целых чисел. В системе программирования должны быть определены следующие операторы:
+ сложение,
- вычитание,
* умножение,
/ деление,
% остаток от целочисленного деления.
В каждой ВМ определено некоторое подмножество целых чисел, лежащих в некоторых пределах. С этим подмножеством компьютер может оперировать прямо и эффективно.
Следует отметить, что, несмотря на внешнюю простоту целого типа, обычные аксиомы арифметики, вообще говоря, нельзя применять к арифметике вычислительной машины. Они не верны в тех случаях, когда истинный результат операции лежит вне заданного конечного диапазона значений.
Например, если для некоторой вычислительной системы тип int определен как множество целых чисел, по абсолютной величине не превосходящих max (|x| <= max), и если сложение, выполняемое машиной, обозначить как add, то
x add y = x + y
только тогда, когда |x + y| <= max. Следовательно, обычный ассоциативный закон, вообще говоря, для машины не выполняется.
Соотношение
(x add y) add z = x add (y add z)
верно тогда, когда |x add y| <= max и |y add z| <= max.
Например, положим max = 100 и подставим значения x = 60, y = 50 и z = -40. Тогда
60 add (50 add (-40)) = 70
а результат (60 add 50) add (-40) не определен.
Количество элементов памяти, необходимое для хранения значений переменной в машине, зависит от диапазона значений, которые эта переменная может принимать. Например, если переменная должна принимать n различных значений, то для ее хранения потребуется log2(n) битов. Я напоминаю Вам этот, конечно же, известный факт по той только причине, что мы привыкли исходить из обратного - то есть, от машинного представления данного типа. Чаще мы рассуждаем так: для хранения переменной целого типа отводится 16 бит. Следовательно, самое большое целое число (то есть граница интервала) есть 65535 (216 - 1). Хотя надо оценивать интервал значений, необходимый нам для моделирования, и под него выбирать тип переменной.
Литерный (char). Этот тип обозначает конечное упорядоченное множество литер. Помимо диапазонов чисел, следует определять набор литер, с помощью которых компьютер общается с внешним миром. Литерами из этого набора снабжаются все периферийные устройства (читающие и печатающие). Существует несколько общепринятых стандартов наборов литер:
Наиболее популярный ASCII код поддерживает 26 букв латинского алфавита, 10 десятичных арабских цифр, некоторое количество специальных литер, таких, как знаки пунктуации. Стандарт определяет 128 литер, подразделяющихся на печатаемые и управляющие. Управляющие литеры играют большую роль при передаче данных. Например, литеры Carriage Return и Line Feed обрабатываются всеми построчно печатающими устройствами. Оставшиеся 128 кодов от 128 до 255 могут определять коды национального языка. Существуют четыре варианта кодировки символов кириллицы. Стандартом de facto стал так называемый альтернативный вариант кодировки. Сейчас, когда отечественная промышленность перестала производить ЭВМ, проблемы нескольких стандартов стали не такими острыми. Хотя еще попадаются файлы с документацией, набранные в основном варианте кодировки. Тогда мы видим по преимуществу, не буквы, а символы псевдографики. А всего лишь несколько лет назад выпускаемые различными министерствами ПЭВМ (скопированные с одного и того же образца IBM PC XT) имели различные кодировки.
Отображение битов на множество литер называется кодом. Следовательно, каждой литере соответствует целое неотрицательной число. Таким образом, тип char можно интерпретировать как множество неотрицательных целых чисел в интервале от 0 до 255, или множество целых чисел в интервале от -128 до +127. Некоторые языки имеют функции преобразования из целого числа в литерное и наоборот (например, BASIC).
Лирическое отступление UNICODE.
Хорошо, когда национальная письменность соотносится с набором литер латинского алфавита. Например, на 26 латинских литер - 33 литеры кириллицы. А каково арабам, китайцам, японцам? Клинопись, иероглифы и языки, в которых столько букв, что одного байта для кодировки не хватает. Для поддержки подобных языков были созданы двухбайтовые наборы символов. Как всегда, было предложено несколько вариантов, и после непродолжительных мучений был выработан стандарт Unicode. Его первоначально разработали фирмы Apple и Xerox в 1988 году. В 1991г был создан консорциум, в который вошли основные производители Hardware и Software. Строки в Unicode просты и логичны. Все символы в них состоят из 16-битовых кодов. Следовательно. Можно закодировать 65536 символов. Этого достаточно даже для японской каны. В настоящее время кодовые позиции определены для нескольких языков и задействовано около 34000 кодов. Так что место для расширения есть. Кодовые позиции разбиты на группы:
0000 - 007F ASCII
0080 - 00FF Расширение ASCII (Latin 1)
0100 - 017F Европейские латинские
0180 - 01FF Расширенные латинские
0250 - 02AF Стандартные фонетические
02B0 - 02FF Модифицированные литеры
0300 - 03FF Общие диакритические знаки
0370 - 03FF Греческий
0400 - 04FF Кириллица
0530 - 058F Армянский
0590 - 05FF Еврейский
0600 - 06FF Арабский
0900 - 097F Деванагари
Около 29000 кодовых позиций пока не занято, но зарезервировано для будущего использования. Приблизительно 6000 позиций оставлено специально для программистов.
Вещественный (real, float, double :) Особое значение имеет тот факт, что в машине можно представить только значения из конечного диапазона. В случае целых чисел можно утверждать, что при любых обстоятельствах, кроме переполнения, в результате выполнения арифметических операций получались точные значения. Но применительно к арифметике с вещественными числами это утверждение неверно.
Причина заключается в том, что на сколь угодно малом интервале оси вещественных чисел содержится бесконечно много значений. Ось вещественных чисел образует так называемый континуум.
В программировании, тип real не представляет бесконечное, несчетное множество вещественных чисел; ему соответствует конечное множество представителей интервалов континуума вещественных чисел.
Результаты вычислений, в которых участвуют приближенные, а не точные значения, в значительной степени зависят от решаемой задачи и от выбранного алгоритма. В лучшем случае полученные результаты будут приближениями с неизбежными погрешностями. Оценка этих погрешностей, которые являются результатом подмены вещественного континуума конечным множеством представителей, является очень трудной задачей и служит объектом изучения вычислительной математики.
Естественно, что производить вычисления, ничего не зная о природе и степени ожидаемой погрешности, бессмысленно. Чтобы понимать и даже проводить такие измерения, необходимо знать, какого вида представления используются для вещественных чисел с конечным числом цифр. В современных компьютерах обычно используют так называемое представление с плавающей точкой (отсюда и пошло название типа float)
Вещественное число x изображается с помощью двух целых чисел e и m, каждое из которых содержит конечное число цифр, так что
x = m * Be, -E < e < E, -M < m < M
Здесь m называется мантиссой, e - порядком, а B, E и M являются константами, характеризующими представление. Число B называется основанием представления с плавающей точкой. Обычно B не равно 10 (как мы привыкли в школьном курсе математики), а является малой степенью 2.
Для любого заданного значения x можно найти много различных пар (m, e). Каноническая или нормализованная форма определяется дополнительным соотношением
(M/B)<=|m|<M
При использовании только нормализованной формы плотность представителей интервалов на оси вещественных чисел экспоненциально уменьшается с увеличением |x|. Например, интервал [0.1:1] содержит приблизительно столько же представителей, сколько интервал [10000 : 100000]. (Для основания представления 10 это точно столько).
Эта неравномерность накладывает ряд ограничений. Очевидно, что нельзя точно описать элементарные операции над аргументами типа float. Поэтому они определяются в виде аксиом:
Следующие аксиомы постулируют совокупность требований, которые безоговорочно должны выполняться в любой машинной арифметике.
x + y = y + x, x * y = y * x
x - y = x + (-y) = - (y - x)
( -x ) * y = x * ( -y ) = - (x * y )
( -x ) / y = x / ( -y ) = - (x / y )
если 0 <= x <= a и 0 <= y <= b
то x + y <= a + b, x - b <= a - y,
x * y <= a * b/ x / b <= a / y
Обратите внимание, что обычные для арифметики законы ассоциативности и дистрибутивности отсутствуют в рассмотренных аксиомах. Это не случайно. Использование конечного числа представителей может нарушить исполнение этих законов. Например, если наше представление позволяет хранить только 4 цифры (порядок будем задавать положением десятичной точки) то
x = 9.900 y = 1.000 z = -0.999
1. (x + y) + z = 10.90 + (-0.999) = 9.910
2. x + (y + z) = 9.900 + 0.001 = 9.901
Вы можете сами представить подобный пример нарушения дистрибутивного закона.
Определенную опасность представляют операции сложения и вычитания. Они являются причиной значительных ошибок, особенно при вычитании почти равных значений. В этом случае большое количество значащих цифр взаимно уничтожаются, и в полученной разности может не оказаться нескольких, а может быть и всех значащих цифр.
Деление также представляет потенциальную опасность. В случае малых делителей результат легко может оказаться в области переполнения. Поэтому следует избегать деления на числа, близкие к нулю. Например, не следует в программе пользоваться отношением вида abs (t/x) <= eps, его лучше заменить отношением вида abs(t) <= eps * abs(x). Оно не содержит деления.
Мне кажется, что рассмотренные положения несколько поколебали Ваши представление о типах как о чем-то простом и понятном. А ведь мы пока говорили только об основных типах. Пора переходить к составным и производным типам.