Максимальное значение типа word
7.1.1. Порядковые типы
К порядковым типам относятся (см. рис. 7.1) целые, логические, символьный, перечисляемый и тип-диапазон. К любому из них применима функция Ord(x), которая возвращает порядковый номер значения выражения X.
Рис. 7.1. Структура типов данных
Для целых типов функция ord(x) возвращает само значение х, т. е. Ord(X) = х для х, принадлежащего любому целому типу. Применение Ord(x) к логическому, символьному и перечисляемому типам дает положительное целое число в диапазоне от 0 до 1 (логический тип), от 0 до 255 (символьный), от 0 до 65535 (перечисляемый). Тип-диапазон сохраняет все свойства базового порядкового типа, поэтому результат применения к нему функции ord(X) зависит от свойств этого типа.
К порядковым типам можно также применять функции:
pred(x) - возвращает предыдущее значение порядкового типа (значение, которое соответствует порядковому номеру
ord (X) -1), т. е. Ord(Pred(X)) = Ord(X) - 1;
succ (X) - возвращает следующее значение порядкового типа, которое соответствует порядковому номеру ord (X) +1, т. е.
Ord(Succ(X)) = Ord(X) + 1.
Например, если в программе определена переменная
то функция PRED(C) вернет символ '4', а функция SUCC(C) - символ '6'.
Если представить себе любой порядковый тип как упорядоченное множество значений, возрастающих слева направо и занимающих на числовой оси некоторый отрезок, то функция pred(x) не определена для левого, a succ (X) - для правого конца этого отрезка.
Целые типы. Диапазон возможных значений целых типов зависит от их внутреннего представления, которое может занимать один, два, четыре или восемь байтов. В табл. 7.1 приводятся названия целых типов, длина их внутреннего представления в байтах и диапазон возможных значений. Таблица 7.1. Целые типы
Типы LongWord и Int64 впервые введены в версии 4, а типы Smallint и Cardinal отсутствуют в Delphi 1. Тип integer для этой версии занимает 2 байта и имеет диапазон значений от -32768 до +32767, т. е. совпадает с Smallint.
При использовании процедур и функций с целочисленными параметрами следует руководствоваться “вложенностью” типов, т. е. везде, где может использоваться word, допускается использование Byte (но не наоборот), в Longint “входит” Smallint, который, в свою очередь, включает в себя Shortint.
Перечень процедур и функций, применимых к целочисленным типам, приведен в табл. 7.2. Буквами b, s, w, i, l обозначены выражения соответственно типа Byte, Shortint, Word, Integer И Longint,
х - выражение любого из этих типов; буквы vb, vs, vw, vi, vl, vx обозначают переменные соответствующих типов. В квадратных скобках указывается необязательный параметр.
Таблица 7.2. Стандартные процедуры и функции, применимые к целым типам
При действиях с целыми числами тип результата будет соответствовать типу операндов, а если операнды относятся к различным целым типам - общему типу, который включает в себя оба операнда. Например, при действиях с shortint и word общим будет тип integer. В стандартной настройке компилятор Delphi не вырабатывает код, осуществляющий контроль за возможной проверкой выхода значения из допустимого диапазона, что может привести к недоразумениям.
Например, при прогоне следующей программы на экране появится значение 0:
procedure TfmExample.bbRunClick(Sender: TObject) ;
k := 65535; // Максимальное значение типа Word
k := k+1; // По правилам математики
k=65536 IbOutput.Caption := IntToStr(k); // На самом деле k=0!
Если активизировать переключатель project | options | Compiler I Range checking и повторить компиляцию с помощью Project | Build All, компилятор вставит в программу код проверки переполнения и при прогоне программы возникнет исключительная ситуация, которую при желании можно соответствующим образом обработать. Замечу, что, если изменить программу следующим образом:
k := 65535; // Максимальное значение типа Word
IbOutput.Caption := IntToStr(k + 1);// Будет выведено 65536
переполнения не произойдет, т. к. 32-разрядный компилятор версий Delphi 32 автоматически преобразует операнды выражения k+i к 4-байтным величинам.
Логические типы. К логическим относятся типы Boolean, ByteBool, Bool, wordBool и LongBool. В стандартном Паскале определен только тип Boolean, остальные логические типы введены в Object Pascal для совместимости с Windows: типы Boolean и ByteBool занимают по одному байту каждый, Bool и WordBool - по 2 байта, LongBool - 4 байта. Значениями логического типа может быть одна из предварительно объявленных констант False (ложь) или True (истина). Для них справедливы правила:
Поскольку логический тип относится к порядковым типам, его можно использовать в операторе цикла счетного типа, например:
for l := False to True do .
Однако необходимо помнить, что в Delphi 32 для Boolean значение
Ord (True) = +1, В ТО Время как для других типов (Bool, WordBool И Т.Д.)
Ord (True) = -1, поэтому такого рода операторы следует использовать с осторожностью!. Например, для версии Delphi 6 исполняемый оператор showMessage (' --- ') в следующем цикле for не будет выполнен ни разу:
for L := False to True do ShowMessage ('--);
Символьный тип. Значениями символьного типа является множество всех символов ПК. Каждому символу приписывается целое число в диапазоне О. 255. Это число служит кодом внутреннего представления символа, его возвращает функция ord.
Для кодировки в Windows используется код ANSI (назван по имени American National Standard Institute - американского института стандартизации, предложившего этот код). Первая половина символов ПК с кодами 0. 127 соответствует таблице 7.3. Вторая половина символов с кодами 128. 255 меняется для различных шрифтов. Стандартные Windows-шрифты Arial Cyr, Courier New Cyr и Times New Roman для представления символов кириллицы (без букв “ё” и “Ё”) используют последние 64 кода (от 192 до 256): “А”. “Я” кодируются значениями 192..223, “а”. “я” - 224. 255. Символы “Ё” и “ё” имеют соответственно коды 168 и 184.
Таблица 7.3. Кодировка символов в соответствии со стандартом ANSI
Сначала приводит A и B , к указанному типу, а потом вычисляет результат A+B .
Приводит указатель A к указанному типу указателя.
Спецификаторы памяти:
(указываются перед типом)
Объявление переменной в виде константы, её можно читать, но нельзя менять, т.к. она хранится в области flash памяти.
Объявление переменной, значение которой может быть изменено без явного использования оператора присвоения =. Используются для работы с прерываниями.
Объявление локальной переменной, значение которой не теряется, между вызовами функции. Если переменная объявлена глобально (вне функций), то её нельзя подключить в другом файле.
Объявление глобальной переменной, которая определена во внешнем файле.
Объявление локальной переменной, значение которой требуется хранить в регистрах процессора, а не в ОЗУ, для обеспечения ускоренного доступа.
Значения некоторых констант:
Ложь, используются вместо 0
Истина, используется вместо 1
Низкий уровень
Высокий уровень
Конфигурация вывода как вход
Конфигурация вывода как выход
Конфигурация вывода как вход с подтяжкой
Передача младшим битом вперёд
Передача старшим битом вперёд
Тактовая частота Arduino в Гц
Число Пи
Половина числа Пи
Два числа Пи
Число Эйлера
Префиксы:
Запись числа в 2ой системе ( 0b 10101)
Запись числа в 2ой системе ( B 10101)
Запись числа в 8ой системе ( 0 12345)
Запись числа в 16ой системе ( 0x 1234A)
Модификаторы:
Число типа long (12345 L )
Число типа long lond (12345 LL )
Число беззнакового типа (12345 U )
Комбинация модификаторов (12345 UL )
Показатель экспоненты (3 E -5 = 3•10-5)
Переменные, массивы, объекты, указатели, ссылки, . :
Это указание имени и типа переменной.
int A; // объявление переменной А
Это выделение памяти под переменную.
A =1; // определение ранее объявленной A
Действуют постоянно, в любом месте кода.
Создаются внутри функций, циклов и т.д.
удаляются из памяти при выходе из них.
Указывается в одинарных кавычках.
char A=' Z '; // присвоение символа «Z»
Указывается в двойных кавычках.
String A ; // присвоение строки «XY»
Это переменная с указанием класса, вместо типа, через объект можно обращаться к методам класса
Ссылка, это альтернативное имя переменной, она возвращает значение переменной, на которую ссылается.
int A=5; // создана переменная A = 5
int & C=A; // создана ссылка C на переменную A
A++; C++; // в результате A=7 и C=7
// Ссылку нельзя переопределить: &C=Z;
Указатель, это переменная, значением которой является адрес.
int * Y1=&A; // указателю Y1, передан адрес переменной A
int ( * Y2)(int)=F; // указателю Y2, передан адрес функции F
B=Y1; // получаем адрес переменной A из указателя Y1
B= * Y1; // получаем значение A разыменовывая указатель
// Указатель можно переопределять: Y1=&Z;
Создание альтернативного имени для типа
typedef bool dbl; // создаём свой тип «dbl», как тип bool
dbl A=1; // создаём переменную A типа bool
Это переменная состоящая из нескольких однотипных элементов, доступ к значениям которых осуществляется по их индексу.
int A[5]; // объявлен массив A из 5 элементов типа int
int A[2]=; // объявлен и определён массив A из 2 эл-тов
char A[ ]="Hi"; // создана строка A, как массив символов
Это объединение нескольких переменных под одним именем.
struct < int A=5; float B=3; >C; // создана структура «С»
int D = C.A; // получаем значение переменной A структуры С
int Z = C.A; // присваиваем Z значение A структуры С
Значения некоторых констант:
Ложь, используются вместо 0
Истина, используется вместо 1
Операторы:
Циклы:
for( инициализация ; условие ; приращение )
Инициализация - выполняется однократно. Условие - проверяется перед каждым проходом. Приращение - выполняется после каждого прохода. Код в теле цикла - выполняется, если выполняется условие.
while ( условие )
Условие - проверяется перед каждым проходом. Код в теле цикла - выполняется, если выполняется условие.
do < тело цикла; >while ( условие );
Условие - проверяется после каждого прохода. Код в теле цикла - выполняется, если выполняется условие.
Условные операторы:
if ( условие )
Код в теле оператора - будет выполнен если выполняется условие.
if ( условие ) < тело1; >else
Если условие выполняется, то будет выполнен код тела1, иначе, код тела2.
if ( условие1 ) < тело1; >else if ( условие2 )
Если выполняется условие1, то будет выполнен код тела1, иначе, если выполняется условие2, то будет выполнен код тела2. Можно указать любое количество условий
переменная = условие ? значение1 : значение2 ;
Если условие выполняется, то оператор ?: вернёт значение1, иначе, значение2. Аналог конструкции: if( условие )else
switch (переменная) < // проверяемое значение
case константа1: код; break; // сравнение и код
case константа2: код; break; // сравнение и код
default: код; // не обязательно
>
Будет выполнен весь код, который следует после константы, значение которой совпало с переменной. Если совпадений нет, то выполняется весь код, который следует после, необязательного, ключевого слова default. Оператор break описан ниже.
Прерывающие операторы:
Прерывает выполнение кода, в теле любого цикла или оператора switch . case, завершая его выполнение.
Прерывает выполнение кода в теле любого цикла, но не завершает его выполнение, а переходит к следующему проходу этого цикла.
void функция(параметры)
Прерывает выполнение кода в теле функции и передаёт управление коду, вызвавшему эту функцию. После оператора return можно указать значение, которое вернёт функция.
метка : . код ; . goto метка ;
Прерывает выполнение кода и передаёт управление коду, следующему за меткой.
Получение адреса и разыменование указателя:
B=&C; // значением B станет адрес переменной C.
(взятие адреса) Возвращает адрес переменной или функции, а не значение.
B=*C; // значением B станет значение на которое ссылается указатель C.
(разыменование) Возвращает значение переменной по указателю, а не её адрес.
Директивы:
Арифметические:
Присвоение
int A = 18; // A = 18
Сложение
int A = 18 + 4; // A = 22
Умножение
int A = 18 * 4; // A = 72
Деление
int A = 18 / 4; // A = 4
Остаток от деления
int A = 18 % 4; // A = 2
Операторы сравнения: (возвращают true или false)
Ложь, используются вместо 0
Истина, используется вместо 1
Операторы сравнения: == Если равно bool A = 18 == 4; // A = 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - != Если не равно bool A = 18 != 4; // A = 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Если больше bool A = 18 > 4; // A = 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >= Если больше или равно bool A = 18 >= 4; // A = 1
Тип данных WORD
Здравствуйте.
Хотелось бы использовать тип данных WORD для управления множеством выходов, поскольку для массового изменения очень подходит битовые операции. Но не могу понять как грамотно привязвыать выходы к такой переменной. Получаются совсем уж нелепые конструкции.
Для чего этот тип данных используется на практике, в реальной работе?
Тип данных WORD
egjar писал(а): ↑ 22 мар 2021, 22:39 Для чего этот тип данных используется на практике, в реальной работе?
Тип Word - это целочисленный беззнаковый тип данных, в два байта. Диапазон 0-65535. Используется везде, где оказывается нужным.
Где конкретно, в какой среде, с каким ПЛК, по какому протоколу Вы работете с выходами?
Тип данных WORD
egjar писал(а): ↑ 22 мар 2021, 22:39 Для чего этот тип данных используется на практике, в реальной работе?
Вот для упаковки-распаковки битовых переменных и используется в основном.
И, например, в Step7 тип Word интерпретируется как массив бит.
А как использовать. по разному в различных системах например Var_Bool0:=Var_Word.0, где-то операторы есть Pack- UnPack, а в софте для программирования есть кнопка "HELP", если что не понятно жмем и ищем-читаем там.
Тип данных WORD
stesl писал(а): ↑ 23 мар 2021, 10:37 Тип Word - это целочисленный беззнаковый тип данных, в два байта. Диапазон 0-65535. Используется везде, где оказывается нужным.
Не путайте Word с UInt (unsigned integer16), он не относится к целочисленным, так как не кодирует числовые значения и не совместим с математическими операциями.
Потому что:
Sergy6661 писал(а): ↑ 23 мар 2021, 12:54 Вот для упаковки-распаковки битовых переменных и используется в основном.
Но не в основном, а только для этого. Если конечно в конкретном ПЛК не срабатывает неявное преобразование, из-за которого кажется, что Word - это целое число.
Это разные типы данных, хотя все они 16 бит.
Тип данных WORD
Михайло писал(а): ↑ 23 мар 2021, 21:52 Не путайте Word с UInt (unsigned integer16), он не относится к целочисленным, так как не кодирует числовые значения и не совместим с математическими операциями.
Ну, допустим, в CoDeSys WORD по большему счету как UINT16, но в CoDeSys можно по биту к любому целочисленному обращаться.
В общем все от системы разработки зависит и опять же кнопка "HELP" рулит.
Тип данных WORD
Михайло писал(а): ↑ 23 мар 2021, 21:52 Не путайте Word с UInt (unsigned integer16), он не относится к целочисленным, так как не кодирует числовые значения и не совместим с математическими операциями.
Тип данных WORD
Почему именно беззнаковое? Значения в диапазоне -32767. 32767 в какие-то другие типы укладывается?
[+] В начале было слово.
И слово было - два байта.
Давайте вспомним какими могут быть величины? Об этом говорилось в отдельной статье. Сейчас мы быстренько все впомним. Любая величина может быть трех типов: числовой, символьной и логическй. Раньше мы использовали всего несколько типов (integer, real, boolean ), но пришло время расширить знания.
Все типы я буду оформлять в удобную табличку, хотя некоторые моменты буду пояснять. Таблица будет иметь три столбца: тип, размер в байтах, дапозон значений.
Принято разделять числовые типы на две большие подгруппы: целые и вещественные.
Целые типы
Тип | Размер в байтах | Диапазон значений |
shortint | 1 | -128..127 |
smallint | 2 | -32768..32767 |
integer, longint | 4 | -2147483648..2147483647 |
int64 | 8 | -9223372036854775808..9223372036854775807 |
byte | 1 | 0..250 |
word | 2 | 0..65535 |
longword, cardinal | 4 | 0..4294967295 |
uint64 | 8 | 0..18446744073709551615 |
Вещественные типы
Тип | Размер в байтах | Диапазон значений | Количесвто значащих цифр |
real | 8 | -1.8∙10 308 .. 1.8∙10 308 | 15-16 |
double | 8 | -1.8∙10 308 .. 1.8∙10 308 | 15-16 |
single | 4 | -3.4∙10 38 .. 3.4∙10 38 | 7-8 |
Для удобства в Паскале заведены специальные константы, которые возвращают на свое место минимальное или максимальное значение разных типов. Ниже вы можете посмотреть таблицу, в которую занесены все константы.
Константа(1) | Константа(2) | Значение |
MaxShortInt | shortint.MaxValue | Максимальное значение типа shortint |
MaxByte | byte.MaxValue | Максимальное значение типа byte |
MaxSmallInt | smallint.MaxValue | Максимальное значение типа smallint |
MaxWord | word.MaxValue | Максимальное значение типа word |
MaxInt | integer.MaxValue | Максимальное значение типа integer |
MaxLongWord | longword.MaxValue | Максимальное значение типа longword |
MaxInt64 | int64.MaxValue | Максимальное значение типа int64 |
MaxUint64 | uint64.MaxValue | Максимальное значение типа uint64 |
MaxDouble | double.MaxValue | Максимальное значение типа double |
MinDouble | real.Epsilon | Минимальное значение типа double |
MaxReal | real.MaxValue | Максимальное значение типа real |
MinReal | real.Epsilon | Минимальное значение типа real |
MaxSingle | single.MaxValue | Максимальное значение типа single |
MinSingle | single.Epsilon | Минимальное значение типа single |
Логический тип
Символьный тип и строки
Символьный тип
Символьный тип char занимает 2 байта и хранит Unicode-символ.
Chr(n) — функция, возвращающая символ с кодом n в кодировке Windows;
Ord(с) — функция, возвращающая значение типа byte, представляющее собой код символа c в кодировке Windows.
ChrUnicode(w) — возвращает символ с кодом w в кодировке Unicode;
OrdUnicode(с) — возвращает значение типа word, представляющее собой код символа c в кодировке Unicode.
Давайте поэкспериментируем, составим вот такую небольшую программку.
Этой программой я хочу проверить накладываются ли друг на друга кодировка Windows и Unicode. Ниже, на скриншоте вы можете посмотреть результат работы программы.
Накладываются. Просто Unicode длиннее, чем кодировка Windows.
Строковой тип
Строки имеют тип string, состоят из набора последовательно расположенных символов char и используются для представления текста.
Строки могут иметь произвольную длину. Строки изменяемы.
Перечислимый и диапазонный типы
Перечислимый тип
Перечислимый тип определяется упорядоченным набором идентификаторов. Чтобы объявить новый тип, используется специальное слово — type. Его необходимо ставить перед var.
Значения перечислимого типа занимают 4 байта. Каждое значение value представляет собой константу типа typeName, попадающую в текущее пространство имен.
К константе перечислимого типа можно обращаться непосредственно по имени, а можно использовать запись typeName.value, в которой имя константы уточняется именем перечислимого типа, к которому она принадлежит:
Для значений перечислимого типа можно использовать функции Ord, Pred и Succ, а также процедуры Inc и Dec (Подробнее про эти функции и процедуры). Функция Ord возвращает порядковый номер значения в списке констант соответствующего перечислимого типа, нумерация при этом начинается с нуля.
Результат работы программы
Диапазонный тип
Диапазонный тип представляет собой подмножество значений целого, символьного или перечислимого типа и описывается в виде a..b, где a — нижняя, b — верхняя граница интервального типа, a.
Результат работы программы
Тип, на основе которого строится диапазонный тип, называется базовым для этого диапазонного типа. Значения диапазонного типа занимают в памяти столько же, сколько и значения соответствующего базового типа.
На сегодня все! Урок получился объемным, но, надеюсь, понятным. Если же у вас возникли вопросы, напишите нам.
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.
Фундаментальные целочисленные типы
У прочих встроенных типов данных нет каких-либо особенностей, поэтому ниже я дам просто сводную таблицу в которой будут описаны эти типы данных.
Сводная таблица типов
- Хранение целочисленных значений.
- Различные математические вычисления.
Вещественные типы данных
Переменные вещественных типов данных, как следует из их названия, могут хранить значения с десятичной запятой. Все вещественные типы делятся на типы с плавающей или фиксированной запятой.
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 знаков после десятичной запятой. Вы удивитесь, но будет выведено не 0.300000. а 0.29999999999999999000. Очень близко к 0.3 но не 0.3. Т.е. число 0.3 (как и многие другие числа) на самом деле не удается представить точно в двоичной форме. Вы можете использовать значение переменной A в вычислениях, даже не замечая того что она содержит не 0.3, и ошибка вычислений будет минимальна. Если вы попытаетесь вывести пользователю значение переменной A, то пользователь скорее всего увидит именно 0.3 т.к. большинство методов с помощью которых выводится числовая информация для пользователя так или иначе при выводе делает округление до определенного количества цифр. Если вы попытаетесь сравнить это число с другим "по правильному", т.е. так, как описано в третьем следствии, то вы получите верный результат т.к. скорее всего ваш Epsilon будет что-то около 0.001 или 0.000001. Это значительно больше расхождения между числом 0.3 и его представлением в переменной типа Real которое мы наблюдаем. Казалось бы со всех сторон мы прикрыты, но помнить об этой особенности надо.
Намного более подробное объяснение всех этих тонкостей можно прочитать в статье: Неочевидные особенности вещественных чисел написанной Антоном Григорьевым.
Сводная таблица вещественных типов
Тип | Диапазон значений | Формат | Значащих цифр | Прочее |
Real | 5e-324..1.7e308 | знаковый, 8 байт | 15..16 | общий |
Real48 | 2.9e-39..1.7e38 | знаковый, 6 байт | 11..12 | фундаментальный |
Single | 1.5e-45..3.4e38 | знаковый, 4 байт | 7..8 | фундаментальный |
Double | 5e-324..1.7e308 | знаковый, 8 байт | 15..16 | фундаментальный, фактически эквивалентен Real |
Extended | 3.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
Предоставляет большую точность, однако менее переносим на другие платформы. Нужно быть осторожным и стараться не использовать его для записи в файлы которые могут быть потом использованы на других платформах.
Примечание:
Честно говоря эту фразу я сдул прямо из борладновского хелпа, слабо понимая что она значит и почему все именно так а не иначе. :-)
Устаревший тип данных. В действительности хранится как 64-ех битное целое число, однако трактуется как вещественное т.к. к нему неприменимы операции которые обычно могут быть выполнены над целыми числами. Например получение следующего или предыдущего значений. Вместо него где только возможно нужно использовать Int64.
Currency
Фиксированная десятичная точка. В действительности хранится как 64-ех разрядное целое число с четырьмя наименее значимыми цифрами неявно считающимися дробной частью. Используется для выражения денежных величин. Финансовые вычисления с точностью до 4-ех знаков после десятичной точки - общепринятая мировая практика позволяющая снизить ошибки при расчетах.
Резюме про вещественные типы данных
Из всего этого словоизвержения про мантиссы, экспоненты, количество значащих цифр, особенности типов вы должны были сделать вывод. А вывод такой: "Для математических вычислений следует выбирать типы данных согласуя характер расчетов с возможностями и особенностями имеющихся типов данных. Неверный выбор в конечном счете может существенно повлиять на результаты расчетовю. Если они у вас вообще будут."
Арифметические операции
- Сложение "+"
- Вычитание "-"
- Умножение "*"
- Деление "/"
- Деление нацело "div"
- Остаток от деления нацело "mod"
- 4+5 - сложение двух констант 4 и 5
- A*9/C - значение в переменной (или константе) по имени A умножить на девять и результат разделить на значение находящееся в переменной (или константе) по имени C.
- Z div 10+1 - разделить значение Z на десять нацело и прибавить к результату 1
Приоритет операций
- Порядок вычисления для операций с равным приоритетом - слева направо.
- Умножение и деление имеют равный но больший приоритет (вычисляются раньше) чем сложение и вычитание, которые в свою очередь имеют тоже равный приоритет. Приоритет деления нацело и взятия остатка от деления равны приоритету умножения и вычитания.
- При необходимости приоритет может быть изменен с помощью расстановки круглых скобок.
- 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.
Примечание:
Была у меня когда-то японская книжка, автор которой исходил из главной идеи что программированию будут учиться полные дауны. Там вся эта математика разжевывалась так что не понять было невозможно. Со схемами и сценками из жизни японцев. Жаль не сохранилась, мне очень помогла. :-)
Совместимость типов данных
Рассмотрим простейший пример программы в котором проявляется проблема совместимости типов данных. Что мы здесь видим? Во первых, вещественной переменной B присваивается целочисленное значение 47 являющееся результатом сложения двух целочисленных констант 15 и 32. Т.е. здесь мы можем наблюдать неявное преобразование типа выражения 15+32 из целочисленного в вещественный, так чтобы вещественное значение 47.0 можно было разместить в вещественной переменной B. Во вторых, мы наблюдаем попытку присвоить целочисленной переменной A вещественный результат вычисления выражения B/3. Если первый оператор присваивания не вызовет возражений со стороны компилятора, то на второй оператор будет выдана ошибка компиляции.
Рассмотрим второй пример программы. Здесь мы наблюдаем попытку присвоения переменной B значения которое выходит за допустимый диапазон значений для типа данных Real. Этот код так-же вызовет ошибку.
Отсюда правило. Для успешного выполнения присваивания нужно чтобы типы переменной и выражения учавствующие в операторе присваивания были совместимы как в плане преобразования значений из целого в вещественное, так и в плане соответствия значения допустимому диапазону для переменной в которую это значение хотят поместить. Для правильного написания присваивания результатов математических выражений переменным нужно представлять себе какой тип данных будет у результата этого выражения. При вычислении выражений Delphi всегда выбирает такой тип данных, который является общим для всех операндов учавствующих в выражении.
- 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) то во время выполнения программы случится ошибка. То-же самое относится и к вещественным типам данных.
Смотрите также материалы по темам: [Типы данных]
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.
Читайте также: