Королевство Дельфи"Knowledge itself is power"
F.Bacon
 Лицей
  
Главная
О лицее

Список семинаров

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  18:09[Войти] | [Зарегистрироваться]

Типы данных

Andrew Fionik
дата публикации 09-04-2004 15:57

урок из цикла: Азы Delphi.


предыдущий урок содержание семинара следующий урок

Типы данных

Итак, в предыдущей лекции мы примерно представили что из себя представляют типы данных и какие они бывают. В этой лекции мы более подробно разберем встроенные в Delphi типы данных.

Pascal, Object Pascal а за ними и Delphi всегда являлись строго типизированными языками, а это очень важное и полезное свойство языка. Когда для переменных определен тип данных к которому они принадлежат, компилятор имеет возможность проверить правильность операций производимых над этими переменными на этапе компиляции. Таким образом, компилятором производится как-бы первичное тестирование вашего кода. Однако, будучи языком строго типизированным, Delphi имеет механизмы позволяющие программисту определить "свои правила игры" и проинструктировать компилятор чтобы тот трактовал переменные не так, как положено их трактовать согласно их типу, а так, как указывает программист. Такой подход чем-то напоминает электрическую мясорубку. Запихивайте туда мясо согласно инструкции и она благополучно выплюнет фарш из которого ваша мамочка, жена или подруга нажарит вкуснючих котлет. Но если снимете предохранительный кожух и неловко дернетесь, то фарш будет из ваших пальцев.

Целочисленные типы данных

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

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

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

Общие целочисленные типы

Integer

Тип данных Integer общий (generic) знаковый тип. Если помните предыдущую лекцию, то там объяснялось что общие типы данных - платформозависимые. Так вот и Integer на разных платформах имеет разный диапазон значений и занимает разный объем памяти, соответствующий одному машинному слову. На 32-ух разрядных системах Integer может содержать содержать значения от -2147483648 до 2147483647. Почему именно такой диапазон? Все очень просто, это минимальное и максимальные значения которые можно выразить (закодировать) 32-мя бинарными разрядами.Далее, когда снова попадется общий тип данных, я буду указывать его параметры именно для 32-ух разрядных систем. Какими они были на старых системах не очень-то и интересно, а какими они будут сами догадаетесь, не маленькие уже.

Cardinal

Общий (чуете что это значит?) беззнаковый тип. Может содержать значения от 0 до 4294967295.

Фундаментальные целочисленные типы

У прочих встроенных типов данных нет каких-либо особенностей, поэтому ниже я дам просто сводную таблицу в которой будут описаны эти типы данных.

Сводная таблица типов

ТипДиапазон значенийФорматПрочее
Integer-2147483648..2147483647знаковый, 32-битобщий
Cardinal0..4294967295беззнаковый, 32-битобщий
ShortInt-128..127знаковый, 8-битфундаментальный
Byte0..255беззнаковый, 8-битфундаментальный
SmallInt-32768..32767знаковый, 16-битфундаментальный
Word0..65535беззнаковый, 16-битфундаментальный
LongInt-2147483648..2147483647знаковый, 32-битфундаментальный
LongWord0..4294967295беззнаковый, 32-битфундаментальный
Int64-2^63..2^63-1знаковый, 64-битфундаментальный

Так в чем же сермяжная правда этих целочисленных типов? Зачем они нужны? Нужны они затем чтобы объявлять переменные которые участвуют в следующих делах:

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

Вещественные типы данных

Переменные вещественных типов данных, как следует из их названия, могут хранить значения с десятичной запятой. Все вещественные типы делятся на типы с плавающей или фиксированной запятой.

NB! Тут следует обратить внимание на некоторые расхождения в терминологии. У нас считается что дробную часть от целой разделяет запятая. У "них" (америкосов) - точка. Соответственно по отношению к вещественным типам данных говорится floating point (плавающая точка) и fixed point (фиксированная точка). В частности в Delphi, для записи вещественных значений в тексте программы, используется именно точка а не запятая. Однако, внутренний формат записи вещественных чисел никак не относится к их представлению пред ясны очи пользователя у которого согласно давней доброй традиции целая часть от десятичной в его родной зимбабве может отделяться буквой "зю".

Для вещественных чисел с плавающей точкой данные хранятся в экспоненциальном формате. Т.е. хранятся мантисса и экспонента (не помню уже преподают это в школе или нет, но на всякий случай объясню). Например число 12345.6789 хранится в виде 1.23456789*10^4 (1.23456789 умножить на десять в степени 4). Мантисса это число 1.23456789 а экспонента это 4, то в какую степень возводится 10.
Вы спросите: "Что за тавтология!? Опять-же записываем десятичную дробь с помощью десятичной точки. Та же фигня только в профиль".
Отвечаю: "А компьютер про десятичную точку на самом деле ничего не знает, он просто хранит у себя 123456789 и 4 (упакованные конечно в бинарную форму, в один блок памяти фиксированного размера). И всегда считается что десятичная точка находится после первой цифры - в нашем случае между 1 и 2".

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

Пример. Пусть количество значащих цифр 7. Тогда записывая число 987654321 мы сможем записать только 7 цифр из этого числа для представления его в компьютере. Таким образом записать наше число сможем только в приблизительном виде: 9.876543E+8. Все последующие операции с этим значением будут выполнятся уже как с 987654300 а не как с 987654321. Попробуем прибавить к такому числу 1 и получим не 987654322 и не 987654301 а все то-же 987654300 т.к. результат все равно не может быть выражен таким ограниченным количеством значащих цифр.

Второе следствие такого способа хранения вещественных значений - особенности совместного хранения таких значений. Предположим что у нас есть два типа данных, один из которых позволяет хранить 7 значащих цифр а второй 12. Присвоим переменным этих типов значение 123456789. Значение одной переменной будет 1.234567E8 а второй 1.23456789E8. Т.е. получается что значения в этих переменных уже не равны, хотя мы и присваивали им одно и то-же значение.

Ну и третье следствие хранения вещественных значений - сравнение таких значений. Предположим что двумя разными путями вычислялась одна и та-же величина. В одном случае получился результат 1.234 в другом 1.235. Равны ли эти результаты? Вы скажете "нет" и будете правы. Скажете "да" и будете тоже правы. Вопрос состоит в том какая "степень равенства" нас удовлетворяет чтобы признать эти числа равными. Для определения равенства двух вещественных значений обычно используется не собственно результат их сравнения, а соотношение между их разностью и Epsilon - неким достаточно маленьким числом. Т.е. считается что два числа равны если модуль их разности не превышает Epsilon который мы задаем изначально и значение которого мы признаем достаточным чтобы обеспечить приемлемую точность наших расчетов.

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

Четвертая особенность хранения вещественных значений - не все, казалось бы простые значения, могут быть помещены в вещественные переменные в точности в том же виде в котором они фигурировали в тексте программы. Не смотря на то что и значащих цифр хватает, и число попадает в допустимый диапазон. Например попробуем поместить число 0.3 в переменную типа Real. В процедуре WriteLn числа 21 и 20 указывают формат вывода заставляя печатать значение переменной A используя минимум 21 знак из которых 20 знаков после десятичной запятой.

program floatdemo1;
{$APPTYPE CONSOLE}
var
        A:Real;
begin
        A:=0.3;
        WriteLn(A:21:20);
        ReadLn;
end.
Вы удивитесь, но будет выведено не 0.300000..., а 0.29999999999999999000. Очень близко к 0.3 но не 0.3. Т.е. число 0.3 (как и многие другие числа) на самом деле не удается представить точно в двоичной форме. Вы можете использовать значение переменной A в вычислениях, даже не замечая того что она содержит не 0.3, и ошибка вычислений будет минимальна. Если вы попытаетесь вывести пользователю значение переменной A, то пользователь скорее всего увидит именно 0.3 т.к. большинство методов с помощью которых выводится числовая информация для пользователя так или иначе при выводе делает округление до определенного количества цифр. Если вы попытаетесь сравнить это число с другим "по правильному", т.е. так, как описано в третьем следствии, то вы получите верный результат т.к. скорее всего ваш Epsilon будет что-то около 0.001 или 0.000001. Это значительно больше расхождения между числом 0.3 и его представлением в переменной типа Real которое мы наблюдаем. Казалось бы со всех сторон мы прикрыты, но помнить об этой особенности надо.

Намного более подробное объяснение всех этих тонкостей можно прочитать в статье: Неочевидные особенности вещественных чисел написанной Антоном Григорьевым.

Сводная таблица вещественных типов

ТипДиапазон значенийФорматЗначащих цифрПрочее
Real5e-324..1.7e308знаковый, 8 байт15..16общий
Real482.9e-39..1.7e38знаковый, 6 байт11..12фундаментальный
Single1.5e-45..3.4e38знаковый, 4 байт7..8фундаментальный
Double5e-324..1.7e308знаковый, 8 байт15..16фундаментальный, фактически эквивалентен Real
Extended3.6e-4951..1.1e4932знаковый, 10 байт19..20фундаментальный
Comp-2^63+1..2^63-1знаковый, 8 байт19..20фундаментальный
Currency-922337203685477.5808..922337203685477.5807знаковый, 8 байт19..20фундаментальный, с фиксированной точкой

Примечания по поводу разных типов данных

Real48

Устаревший тип данных, оставленный только для совместимости. Например, возможно, вам потребуется прочитать какие-нибудь данные из старых файлов созданных очень давно, когда 6-ти байтовый вещественный тип был широко распространен.

Extended

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

Примечание:
Честно говоря эту фразу я сдул прямо из борладновского хелпа, слабо понимая что она значит и почему все именно так а не иначе. :-)

Comp

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

Currency

Фиксированная десятичная точка. В действительности хранится как 64-ех разрядное целое число с четырьмя наименее значимыми цифрами неявно считающимися дробной частью. Используется для выражения денежных величин. Финансовые вычисления с точностью до 4-ех знаков после десятичной точки - общепринятая мировая практика позволяющая снизить ошибки при расчетах.

Резюме про вещественные типы данных

Из всего этого словоизвержения про мантиссы, экспоненты, количество значащих цифр, особенности типов вы должны были сделать вывод. А вывод такой: "Для математических вычислений следует выбирать типы данных согласуя характер расчетов с возможностями и особенностями имеющихся типов данных. Неверный выбор в конечном счете может существенно повлиять на результаты расчетовю. Если они у вас вообще будут."

Арифметика

Арифметические операции

В Delphi доступно 6 базовых арифметических операций:

  1. Сложение "+"
  2. Вычитание "-"
  3. Умножение "*"
  4. Деление "/"
  5. Деление нацело "div"
  6. Остаток от деления нацело "mod"
Все эти операции являются бинарными (потому что в каждой учавствует два операнда) операциями и записываются как:
Операнд1 Оператор Операнд2
... где Операнд1, Операнд2 могут представлять собой различные выражения, переменные, константы, а Оператор - один из знаков операций: "+ - * / div mod"

Примеры математических выражений:

  • 4+5 - сложение двух констант 4 и 5
  • A*9/C - значение в переменной (или константе) по имени A умножить на девять и результат разделить на значение находящееся в переменной (или константе) по имени C.
  • Z div 10+1 - разделить значение Z на десять нацело и прибавить к результату 1
Как видите запись математических выражений в Delphi слабо отличается от того что писали в школе. Немного попрактикуетесь и поймаете себя на том что записываете на бумаге умножение не как точку или символ "x" а как звездочку "*".

Приоритет операций

При вычислении результатов математических операций Delphi должна руководствоваться каким-то порядком вычисления. Этот порядок называется приоритетом операций. Приоритет операций не отличается от того который мы изучали в школе:

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

Примеры (ну тут вообще первый класс школы... или второй?):

  • A+B-C/6*25 выполняется в следующей последовательности A+B, C/6, C/6*25, A+B-C/6*25.
  • (A+(B-C)/6)*25 Тут последовательность была изменена скобками: B-C, (B-C)/6, A+(B-C)/6, (A+(B-C)/6)*25.

Оператор присваивания

Хотя оператор присваивания переменной какого-нибудь значения имеет достаточно широкий смысл, однако в случае математики оператор присваивания позволяет сохранять результаты вычислений внутри переменных. Формат оператора присваивания:

Переменная:=Выражение;
... где Переменная - идентификатор объявленной переменной а Выражение одно из вышеупомянутых математических выражений.

Пример использования оператора присваивания:
  • B:=A/Z; - переменной B присваивается результат деления значения переменной A на значение переменной Z.
  • D:=(A+(B-C)/6)*25; - переменной D присваивается результат вычисления выражения (A+(B-C)/6)*25.

Примечание:
Была у меня когда-то японская книжка, автор которой исходил из главной идеи что программированию будут учиться полные дауны. Там вся эта математика разжевывалась так что не понять было невозможно. Со схемами и сценками из жизни японцев. Жаль не сохранилась, мне очень помогла. :-)

Совместимость типов данных

Рассмотрим простейший пример программы в котором проявляется проблема совместимости типов данных.

program TypeCompatibility1;
var
  A:Integer;
  B:Real;
begin
  B:=15+32;
  A:=B/3;
end.

Что мы здесь видим? Во первых, вещественной переменной B присваивается целочисленное значение 47 являющееся результатом сложения двух целочисленных констант 15 и 32. Т.е. здесь мы можем наблюдать неявное преобразование типа выражения 15+32 из целочисленного в вещественный, так чтобы вещественное значение 47.0 можно было разместить в вещественной переменной B. Во вторых, мы наблюдаем попытку присвоить целочисленной переменной A вещественный результат вычисления выражения B/3. Если первый оператор присваивания не вызовет возражений со стороны компилятора, то на второй оператор будет выдана ошибка компиляции.

Рассмотрим второй пример программы.

program TypeCompatibility2;
var
  A,B:Real;
begin
  A:=1.5e308;
  B:=1.5e308+A;
end.

Здесь мы наблюдаем попытку присвоения переменной B значения которое выходит за допустимый диапазон значений для типа данных Real. Этот код так-же вызовет ошибку.

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

Примеры выражений. Если:

var
  A:Integer;
  B:Real;
  C:Extended;

то
  • A+5 - тип результата Integer
  • A+B - тип результата Real
  • A+B*C - тип результата Extended
  • B/A - тут казалось бы достаточно типа Real, но в действительности для Delphi тип результата деления Extended всегда

В случае с присвоением результатов значений надо заметить что вещественные и целочисленные типы данных неявно совместимы внутри самих себя. Попытка присвоить переменной типа Byte значение переменной типа Integer будет синтаксически верной, т.е. не вызовет ошибки компиляции. Ведь может быть в переменной типа Integer лежит значение 5, которое вполне подходит как для Byte так и для Integer. Однако в этом случае компилятор вставляет машинный код который контролирует значение присваиваемое переменной типа Byte. Если это окажется значение не попадающее в диапазон 0..255, (диапазон допустимых значений для Byte) то во время выполнения программы случится ошибка. То-же самое относится и к вещественным типам данных.

Пример:

program TypeCompatibility3;
const
  A=32;
  P:Real=5;
var
  B:Integer;
  C:Extended;
  D:Word;
begin
  B:=A; // Все верно, 32 совместимо с типом Integer.
  B:=P; // Ошибка компиляции, несовместимые типы Integer и Real.
  C:=P; // Все нормально, компилируется и выполняется.
  D:=B*100000; { Ошибка времени выполнения, результат 32*100000 выходит
  за пределы допустимых значений для типа Word. }
  B:=B*100000; { Все нормально, тип результата Integer и он входит в диапазон
  допустимых значений для переменных типа Integer.}
  B:=B/2; { Ошибка компиляции. Независимо от того какой-там получится
  результат выражения B/2, тип результата будет вещественный.}
  C:=B*3; {Все в порядке. Результат B*3 - целочисленное значение B которое
  неявно преобразуется в Extended, это допустимо.}
end.


предыдущий урок содержание семинара следующий урок




Смотрите также материалы по темам:
[Типы данных]

 Обсуждение материала [ 27-06-2005 01:43 ] 8 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования