Extended — Тип Delphi


Содержание

Extended syntax (Delphi)

Note: The $X directive is provided for backward compatibility. You should not use the <$X->mode when writing Delphi applications.

The $X directive enables or disables Delphi’s extended syntax:

  • Function statements. In the <$X+>mode, function calls can be used as procedure calls; that is, the result of a function call can be discarded, rather than passed to another function or used in an operation or assignment. Generally, the computations performed by a function are represented through its result, so discarding the result makes little sense. Sometimes, however, a function is called because it performs a task such as setting the value of a global variable, without producing a useful result.
  • The Result variable. In the <$X+>mode, the predefined variable Result can be used within a function body to hold the function’s return value.
  • Null-terminated strings. In the <$X+>mode, Delphi strings can be assigned to zero-based character arrays (array[0..X] of Char), which are compatible with PChar types.

This directive also affects the use of the caret (^) to indicate pointers. When <$X+>is in effect (the default), you can omit the caret when referencing pointers. When declaring a pointer, however, the caret is required.

Normally, the line marked #1 would be expressed as:

But with extended syntax enabled, the caret can be omitted.

Extended — Тип Delphi

3.2.2. Вещественные типы Delphi

В Delphi существует четыре вещественных типа: Single , Double , Extended и Real . Их общий формат одинаков (рис. 3.1, а).

Знак — это всегда один бит. Он равен нулю для положительных чисел и единице для отрицательных. Что же касается размеров мантиссы и экспоненты, то именно в них и заключается различие между типами.

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

а) общий вид вещественного числа

б) Двоичное представление числа типа Single

Рис. 3.1. Хранение вещественного числа в памяти

Микропроцессор Intel 8086/88 и его улучшенные варианты — 80286 и 80386 — также не имели аппаратной поддержки вещественных чисел. Но у систем на базе этих процессоров была возможность подключения так называемого сопроцессора. Эта микросхема работала с памятью через шины основного процессора и обеспечивала аппаратную поддержку вещественных чисел. В системах средней руки гнездо сопроцессора обычно было пустым, т.к. это уменьшало цену (разумеется, вставить туда сопроцессор не было проблемой). Для каждого центрального процессора выпускались свои сопроцессоры, маркировавшиеся Intel 8087, 80287 и 80387 соответственно. Были даже сопроцессоры, выпускаемые другими фирмами. Они работали быстрее, чем сопроцессоры Intel, но появлялись на рынке позже. Тип вещественных чисел, поддерживаемый сопроцессорами, не совпадает с Real . Он определяется стандартом IEEE (Institute of Electrical and Electronics Engineers).

Чтобы обеспечить в своих системах поддержку типов IEEE, Borland вводит в Turbo Pascal типы Single , Double и Extended . Extended — это основной для сопроцессора тип, a Single и Double получаются из него очень простым усечением. Система команд сопроцессора допускает работу с этими типами: при загрузке числа типа Single или Double во внутренний регистр сопроцессора последний конвертирует их в Extended . Напротив, при выгрузке чисел этих типов из регистра в память сопроцессор усекает их до нужного размера. Внутренние же операции всегда выполняются с данными типа Extended (впрочем, из этого правила есть исключение, на котором мы остановимся позже, после детального рассмотрения формата различных типов). Single и Double позволяют экономить память. Ни один из них также не совпадает с типом Real . В системах с сопроцессорами новые типы обрабатываются заметно (в 2–3 раза) быстрее, чем Real (это с учетом того, что тип Real после соответствующего преобразования также обрабатывался сопроцессором; если же сравнивать обработку типа Extended на машине с сопроцессором и Real на машине без сопроцессора, то там на отдельных операциях достигалась разница в скорости примерно в 100 раз). Чтобы программы с этими типами можно было выполнять и в системах без сопроцессора, была предусмотрена возможность подключать к ним программный эмулятор сопроцессора. Обработка этих типов эмулятором была медленнее, чем обработка Real .

Начиная с 486-й серии Intel берет курс на интеграцию процессора и сопроцессора в одной микросхеме. Процент брака в микросхемах слишком велик, поэтому Intel идет на хитрость: если у микросхемы брак только в сопроцессорной части, то на этом кристалле прожигаются перемычки, блокирующие сопроцессор, и микросхема продается как процессор 80486SX, не имеющий встроенного сопроцессора (в отличие от полноценной версии, которую назвали 80486DX). Бывали и обратные ситуации, когда сопроцессор повреждений не имел, зато процессор был неработоспособен. Такие микросхемы превращали в «сопроцессор 80487». Но это уже из области экзотики, и, по имеющейся у нас информации, до России такой сопроцессор не дошел.

Процессор Pentium во всех своих вариантах имел встроенный блок вычислений с плавающей точкой (FPU — Floating Point Unit), и отдельный сопроцессор ему не требовался. Таким образом, с приходом этого процессора тип Real остался только для обратной совместимости, а на передний план вышли типы Single , Double и Extended . Начиная с Delphi 4, тип Real становится синонимом типа Double , а старый 6-байтный тип получает название Real48 .

Здесь и далее под словом Real мы будем понимать старый 6-байтный тип.

Существует директива компилятора <$REALCOMPATIBILITY ON/OFF>, при включении которой (по умолчанию она отключена) Real становится синонимом Real48 , а не Double .

Размеры полей для различных вещественных типов указаны в табл. 3.1.

Таблица 3.1. Размеры полей в вещественных типах

Тип Размер типа, байты Размер мантиссы, биты Размер экспоненты, биты
Single 4 23 8
Double 8 52 11
Extended 10 64 15
Real 6 40 7

Другие параметры вещественных типов, такие как диапазон и точность, можно найти в справке Delphi.

3.2.3. Внутренний формат вещественных чисел

Рассмотрим тип Single , т.к. он самый короткий и, следовательно, самый простой для понимания. Остальные типы отличаются от него только количественно. В дальнейшем числа в формате Single мы будем записывать как s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm, где s означает знаковый бит, е — бит экспоненты, m — бит мантиссы. Порядок хранения битов в типе Single показан на рис. 3.1, б (по принятым в процессорах Intel правилам байты в многобайтных значениях переставляются так. что младший байт идет первым, а старший — последним, и вещественных чисел это тоже касается В мантиссе хранится двоичное число. Чтобы получить истинное значение мантиссы, к ней надо мысленно добавить слева единицу с точкой (т.е., например, мантисса 1010000000000000000000 означает двоичную дробь 1.101). Таким образом, имея 23 двоичных разряда, мы записываем числа с точностью до 24-х двоичных разрядов.

Экспонента — по определению всегда целое число. Но способ записи экспоненты в вещественных числах не совпадает с рассмотренным ранее способом записи чисел со знаком. Ноль в этом представлении записывается как 01111111 (в обычном представлении это равно 127). Соответственно. 10000000 (128 в обычном представлении) означает единицу, а 01111110 (126) означает -1, и т. д. (т.е. из обычного беззнакового числа надо вычесть 127, и получится число, закодированное в экспоненте). Такая запись чиста называется нормализованной .

Из описанных правил есть исключения. Так, если все биты экспоненты равны нулю (т.е. там стоит число -127), то к мантиссе перед ее началом надо добавлять не «1.», а «0.» (денормализованная запись) . Это позволяет увеличить диапазон вещественных чисел. Если бы этого исключения не было, то минимально возможное положительное число типа Single было бы равно примерно 5,9·10 -39 . А так появляется возможность использовать числа до 1,4·10 -45 . Побочным эффектом этого является то, что числа, меньшие чем 1,17·10 -38 , представляются с меньшей, чем 24 двоичных разряда, точностью. Если все биты в экспоненте равны единице, а в мантиссе — нулю, то мы получаем комбинацию, известную как INF (от англ. Infinity — бесконечность). Эта комбинация используется тогда, когда результат вычислений превышает максимально допустимое форматом число. В зависимости от значения бита s бесконечность может быть положительной или отрицательной. Если же при такой экспоненте в мантиссе хоть один бит не равен нулю, такая комбинация называется NAN (Not A Number — не число). Попытки работы с комбинациями NAN или INF приводят к ошибке времени выполнения.

Для задания нуля все биты мантиссы и экспоненты должны быть равны нулю (формально это означает 0·10 -127 ). С учетом описанных правил, если хотя бы один бит экспоненты не будет равен нулю (т.е. экспонента будет больше -127), запись будет считаться нормализованной, и нулевая мантисса будет рассматриваться как единица. Поэтому никакие другие комбинации значений мантиссы и экспоненты не могут дать ноль.

А.Б. Григорьев: О чём не пишут в книгах по Delphi 1
Введение 1
Глава 1: Windows API и Delphi 2
1.1. Основы работы Windows API в VCL-приложениях 2
1.1.1. Что такое Windows API 2
1.1.2. Как получить справку по функциям Windows API 2
1.1.3. Дескрипторы вместо классов 5
1.1.4. Формы VCL и окна Windows 5
1.1.5. Функции обратного вызова 7
1.1.6. Сообщения Windows 8
1.1.7. Создание окон средствами VCL 10
1.1.8. Обработка сообщений с помощью VCL 11
1.1.9. Сообщения, определяемые пользователем 16
1.1.10. Особые сообщения 17
1.1.11. Графика в Windows API 17
1.1.12. ANSI и Unicode 20
1.1.13. Строки в Windows API 20
1.2. Примеры использования Windows API 22
1.2.1. Пример EnumWnd 22
1.2.2. Пример Line 23
1.2.3. Пример CoordLabel 25
1.2.4. Пример PanelMsg 26
1.2.5. Пример NumBroadcast 27
1.2.6. Пример ButtonDel 28
1.2.7. Пример GDIDraw 28
1.2.8. Пример BitmapSpeed 31
1.3. Обобщающие примеры 33
1.3.1. Обобщающий пример 1 — Информация о процессах 33
1.3.1.1. Получение списка процессов 33
1.3.1.2. Получение списка и свойств окон 33
1.3.2. Обобщающий пример 2 — Ассоциированные файлы и предотвращение запуска второй копии приложения 34
1.3.2.1. Ассоциирование расширения с приложением 34
1.3.2.2. Командная строка 35
1.3.2.3. Поиск уже запущенной копии приложения 35
1.3.2.4. Перевод приложения на передний план 36
1.3.3. Обобщающий пример 3 — «Дырявое» окно 37
1.3.3.1. Сообщение WM_NCHCHITTEST 37
1.3.3.2. Регионы 37
1.3.3.3. Сообщения WM_SIZE и WM_SIZING 37
1.3.3.4. А теперь — все вместе 38
1.3.4. Обобщающий пример 4 — Линии нестандартного стиля 39
1.3.4.1. Получение координат точек прямой 39
1.3.4.2. «Резиновая» линия и растровые операции 40
1.3.4.3. Кривые Безье 40
1.3.4.4. Траектории 41
1.3.4.5. Интерактивная кривая 42
Глава 2: Использование сокетов Delphi 43
2.1. Стандартные сокеты 44
2.1.1 Соглашения об именах 44
2.1.2. Общие сведения о сокетах 44
2.1.3. Сетевые протоколы. Семиуровневая модель OSI 45
2.1.4. Стек TCP/IP 45
2.1.5. Протокол UDP 46
2.1.6. Протокол TCP 47
2.1.7. Сетевые экраны 48
2.1.8. Создание сокета 48
2.1.9. Передача данных при использовании UDP 51
2.1.10. Пример программы: простейший чат на UDP 53
2.1.11. Передача данных при использовании TCP 55
2.1.12. Примеры передачи данных с помощью TCP 56
2.1.13. Определение готовности сокета 59
2.1.14. Примеры использования функции select 61
2.1.15. Неблокирующий режим 63
2.1.16. Сервер на неблокирующих сокетах 65
2.1.17. Параметры сокета 67
2.1.18. Итоги первого раздела 68
2.2. Сокеты Windows 68
2.2.1. Версии Windows Sockets 68
2.2.2. Устаревшие функции WinSock 1 68
2.2.3. Информация о протоколе 69
2.2.4. Новые функции 70
2.2.5. Асинхронный режим, основанный на сообщениях 72
2.2.6. Пример сервера, основанного на сообщениях 74
2.2.7. Асинхронный режим, основанный на событиях 77
2.2.8. Пример использования сокетов с событиями 79
2.2.9. Перекрытый ввод-вывод 84
2.2.10. Сервер, использующий перекрытый ввод-вывод 87
2.2.11. Многоадресная рассылка 89
2.2.12. Дополнительные функции 91
2.3. Итоги главы 92
Глава 3: «Подводные камни» 93
3.1. Неочевидные особенности целых чисел 93
3.1.1. Аппаратное представление целых чисел 93
3.1.2. Выход за пределы диапазона при присваивании 94
3.1.3. Переполнение при арифметических операциях 94
3.1.4. Сравнение знакового и беззнакового числа 95
3.1.5. Неявное преобразование в цикле for 95
3.2. Неочевидные особенности вещественных чисел 95
3.2.1. Двоичные дроби 95
3.2.2. Вещественные типы Delphi 96
3.2.3. Внутренний формат вещественных чисел 96
3.2.4. «Неполноценный» Extended 97
3.2.5. Бесконечные дроби 97
3.2.6. «Неправильное» значение 97
3.2.7. Сравнение 98
3.2.8. Сравнение разных типов 98
3.2.9. Вычитание в цикле 98
3.2.10. Неожиданная потеря точности 98
3.2.11. Борьба с потерей точности в VCL 99
3.2.12. Машинное эпсилон 99
3.2.13. Методы решения проблем 99
3.3. Тонкости работы со строками 100
3.3.1. Виды строк в Delphi 100
3.3.2. Хранение строковых литералов 100
3.3.3. Приведение литералов к типу PChar 101
3.3.4. Сравнение строк 102
3.3.5. Побочное изменение 103
3.3.6. Нулевой символ в середине строки 104
3.3.7. Функция, возвращающая AnsiString 104
3.3.8. Строки в записях 104
3.3.9. Использование ShareMem 107
3.4. Прочие «подводные камни» 108
3.4.1. Порядок вычисления операндов 108
3.4.2. Зацикливание обработчика TUpDown.OnClick при открытии диалогового окна в обработчике 109
3.4.3. Access violation при закрытии формы с перекрытым методом WndProc 109
3.4.4. Подмена имени оконного класса, возвращаемого функцией GetClassInfo 110
3.4.5. Ошибка EReadError при использовании вещественных свойств 111
3.4.6. Ошибка List index out of bounds при корректном значении индекса 111
3.4.7. Неправильное поведение свойства Anchors 112
3.4.8. Ошибка при сравнении указателей на метод 112
3.4.9. Возможность получения адреса свойства 113
3.4.10. Невозможность использования некоторых свойств оконного компонента в деструкторе 113
Глава 4: Разбор и вычисление выражений 115
4.1. Синтаксис и семантика 115
4.2. Формальное описание синтаксиса 116
4.3. Синтаксис вещественного числа 117
4.4. Простой калькулятор 117
4.5. Учет приоритета операторов 118
4.6. Выражения со скобками 119
4.7. Полноценный калькулятор 120
4.8. Калькулятор с лексическим анализатором 121
4.9. Однопроходный калькулятор и функции с несколькими переменными 123
4.10. Еще немного теории 125
Приложение 1: Сайт «Королевство Delphi» 126
Приложение 2: Содержимое компакт-диска 128
Примеры к главе 1 128
Примеры к главе 2 129
Примеры к главе 3 129
Примеры к главе 4 130
Список литературы 130

Лучшие электронные книги в формате fb2
Наш портал – это библиотека интересных электронных книг разнообразных жанров. Здесь вы найдете произведения как российских, так и зарубежных писателей. Все электронные книги, представленные на нашем сайте, можно скачать бесплатно. Наша библиотека содержит только лучшие бесплатные электронные книги, ведь каждую электронную книгу мы тщательно изучаем перед добавлением в базу. Мы выбираем интереснейшие произведения в удобном формате fb2, все они достойны вашего внимания. Чтение электронных книг наверняка принесет вам удовольствие. Всё что, что вам нужно сделать, — найти и скачать книгу, которая понравится вам по заголовку и описанию.
Библиотека fb2-электронных книг – полезнейшее изобретение человечества. Для того чтобы, читать книгу, вам нужно просто загрузить ее с нашего сайта. Вы можете наслаждаться чтением, не совершая лишние траты. Электронная книга, в отличие от бумажной, обладает множеством преимуществ. Вы экономите время и силы, не совершая утомительные походы по магазинам. Вам также не нужно обременять себя ношением тяжеловесной макулатуры. Скачать и читать электронную книгу легко и просто . Мы позаботились о том, чтобы вам всегда было что почитать. Электронная книга fb2 принесет вам море положительных эмоций: она способна поделиться с вами мудростью, поднять настроение или просто скрасить досуг.

3.2.2. Вещественные типы Delphi

3.2.2. Вещественные типы Delphi


В Delphi существует четыре вещественных типа: Single , Double , Extended и Real . Их общий формат одинаков (рис. 3.1, а).

Знак — это всегда один бит. Он равен нулю для положительных чисел и единице для отрицательных. Что же касается размеров мантиссы и экспоненты, то именно в них и заключается различие между типами.

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

а) общий вид вещественного числа

б) Двоичное представление числа типа Single

Рис. 3.1. Хранение вещественного числа в памяти

Микропроцессор Intel 8086/88 и его улучшенные варианты — 80286 и 80386 — также не имели аппаратной поддержки вещественных чисел. Но у систем на базе этих процессоров была возможность подключения так называемого сопроцессора. Эта микросхема работала с памятью через шины основного процессора и обеспечивала аппаратную поддержку вещественных чисел. В системах средней руки гнездо сопроцессора обычно было пустым, т. к. это уменьшало цену (разумеется, вставить туда сопроцессор не было проблемой). Для каждого центрального процессора выпускались свои сопроцессоры, маркировавшиеся Intel 8087, 80287 и 80387 соответственно. Были даже сопроцессоры, выпускаемые другими фирмами. Они работали быстрее, чем сопроцессоры Intel, но появлялись на рынке позже. Тип вещественных чисел, поддерживаемый сопроцессорами, не совпадает с Real . Он определяется стандартом IEEE (Institute of Electrical and Electronics Engineers).

Чтобы обеспечить в своих системах поддержку типов IEEE, Borland вводит в Turbo Pascal типы Single , Double и Extended . Extended — это основной для сопроцессора тип, a Single и Double получаются из него очень простым усечением. Система команд сопроцессора допускает работу с этими типами: при загрузке числа типа Single или Double во внутренний регистр сопроцессора последний конвертирует их в Extended . Напротив, при выгрузке чисел этих типов из регистра в память сопроцессор усекает их до нужного размера. Внутренние же операции всегда выполняются с данными типа Extended (впрочем, из этого правила есть исключение, на котором мы остановимся позже, после детального рассмотрения формата различных типов). Single и Double позволяют экономить память. Ни один из них также не совпадает с типом Real . В системах с сопроцессорами новые типы обрабатываются заметно (в 2–3 раза) быстрее, чем Real (это с учетом того, что тип Real после соответствующего преобразования также обрабатывался сопроцессором; если же сравнивать обработку типа Extended на машине с сопроцессором и Real на машине без сопроцессора, то там на отдельных операциях достигалась разница в скорости примерно в 100 раз). Чтобы программы с этими типами можно было выполнять и в системах без сопроцессора, была предусмотрена возможность подключать к ним программный эмулятор сопроцессора. Обработка этих типов эмулятором была медленнее, чем обработка Real .

Начиная с 486-й серии Intel берет курс на интеграцию процессора и сопроцессора в одной микросхеме. Процент брака в микросхемах слишком велик, поэтому Intel идет на хитрость: если у микросхемы брак только в сопроцессорной части, то на этом кристалле прожигаются перемычки, блокирующие сопроцессор, и микросхема продается как процессор 80486SX, не имеющий встроенного сопроцессора (в отличие от полноценной версии, которую назвали 80486DX). Бывали и обратные ситуации, когда сопроцессор повреждений не имел, зато процессор был неработоспособен. Такие микросхемы превращали в «сопроцессор 80487». Но это уже из области экзотики, и, по имеющейся у нас информации, до России такой сопроцессор не дошел.

Процессор Pentium во всех своих вариантах имел встроенный блок вычислений с плавающей точкой (FPU — Floating Point Unit), и отдельный сопроцессор ему не требовался. Таким образом, с приходом этого процессора тип Real остался только для обратной совместимости, а на передний план вышли типы Single , Double и Extended . Начиная с Delphi 4, тип Real становится синонимом типа Double , а старый 6-байтный тип получает название Real48 .

Здесь и далее под словом Real мы будем понимать старый 6-байтный тип.

Существует директива компилятора <$REALCOMPATIBILITY ON/OFF>, при включении которой (по умолчанию она отключена) Real становится синонимом Real48 , а не Double .

Размеры полей для различных вещественных типов указаны в табл. 3.1.

Таблица 3.1. Размеры полей в вещественных типах

Тип Размер типа, байты Размер мантиссы, биты Размер экспоненты, биты
Single 4 23 8
Double 8 52 11
Extended 10 64 15
Real 6 40 7

Другие параметры вещественных типов, такие как диапазон и точность, можно найти в справке Delphi.

Особенности вещественных чисел в Delphi

Written on 08 Января 2009 . Posted in Delphi

ОГЛАВЛЕНИЕ

Двоичные дроби

Для начала – немного математики. В школе мы проходим два вида дробей – простые и десятичные. Десятичные дроби, по сути дела, представляют собой разложение числа по степеням десяти. Так, запись 13.6704 означает число, равное 1*10 1 +3*10 0 +6*10 -1 +7*10 -2 +0*10 -3 +4*10 -4 . Но внутреннее представление всех чисел в компьютере, в том числе и вещественных – не десятичное, а двоичное. Поэтому используются двоичные дроби. Они во многом похожи на десятичные, но основание степени у них двойка. Так, число 101.1101=1*2 2 +0*2 1 +1*2 0 +1*2 -1 +1*2 -2 +0*2 -3 +1*2 -4 . То есть в десятичном представлении это число равно 5.8125, в чём нетрудно убедиться с помощью любого калькулятора.

Теперь вспомним научный формат записи десятичного числа. Первым в этой записи идёт знак числа – плюс или минус. Дальше идёт так называемая мантисса – число от 1 до 10. Затем идёт экспонента – степень десяти, на которую надо умножить мантиссу, чтобы получить нужное число. Итак, уже упоминавшееся число 13.6704 запишется в этом формате как 1.36704*10 1 (или 1.36704E1 по принятым в компьютере правилам). Если записываемое число меньше единицы, экспонента будет отрицательной. Аналогичная запись существует и в двоичной системе. Так, 101.1101 запишется в виде 1.011101*10 10 (Везде использована двоичная форма записи, так что 10 10 означает 2 2 ). Именно такое представление используется в компьютере. Двоичная точка в такой записи не остаётся на одном месте, а сдвигается на величину, указанную в экспоненте, поэтому такие числа называются числами с плавающей точкой (floating point numbers).

Вещественные типы Delphi

Знак – это всегда один бит. Он равен нулю для положительных чисел и единице для отрицательных. Что же касается размеров мантиссы и экспоненты, то именно в них и заключается различие между типами.

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

Микропроцессор Intel 8086/88 и его улучшенные варианты – 80286 и 80386 – также не имели аппаратной поддержки вещественных чисел. Но системы на базе этих процессоров имели возможность подключения так называемого сопроцессора. Эта микросхема работала с памятью через шины основного процессора и обеспечивала аппаратную поддержку вещественных чисел. В системах средней руки гнездо сопроцессора обычно было пустым, так как это удешевляло систему (разумеется, вставить туда сопроцессор не было проблемой). Для каждого центрального процессора выпускались свои сопроцессоры, маркировавшиеся Intel 8087, 80287 и 80387 соответственно. Были даже сопроцессоры, выпускаемые другими фирмами. Они работали быстрее, чем Intel’овские, но появлялись на рынке позже. Тип вещественных чисел, поддерживаемый сопроцессорами, не совпадает с Real.

Чтобы обеспечить в своих системах поддержку сопроцессорных типов, Borland вводит в Turbo Pascal типы Single, Double и Extended. Extended – это родной для сопроцессора тип, а типы Single и Double получаются из него очень простым усечением. При загрузке числа типа Single или Double во внутренний регистр сопроцессора последний конвертирует их в Extended. Напротив, при выгрузке чисел этих типов из регистра в память сопроцессор усекает их до нужного размера. Внутренние же операции всегда выполняются с данными типа Extended (впрочем, из этого правила есть исключение, на котором мы остановимся позже, после детального рассмотрения формата различных типов). Single и Double используются для экономии памяти. Ни один из них также не совпадает с типом Real. В системах с сопроцессорами новые типы обрабатываются заметно (в 2-3 раза) быстрее, чем Real (это с учётом того, что тип Real после соответствующего преобразования также обрабатывался сопроцессором; если же сравнивать обработку типа Extended на машине с сопроцессором и Real на машине без сопроцессора, то там на отдельных операциях достигалась разница примерно в 100 раз). Чтобы программы с этими типами можно было выполнять и в системах без сопроцессора, была возможность подключать к ним программный эмулятор сопроцессора. Обработка этих типов эмулятором была медленнее, чем обработка Real.

Начиная с 486-ого процессора Intel берёт курс на интеграцию процессора и сопроцессора в одной микросхеме. Процент брака в микросхемах слишком велик, поэтому Intel идёт на хитрость: если у микросхемы брак только в сопроцессорной части, то на этой микросхеме прожигаются перемычки, блокирующие сопроцессор, и микросхема продаётся как процессор 80486SX, не имеющий встроенного сопроцессора (в отличие от полноценной версии, которую назвали 80486DX). Бывали и обратные ситуации, когда сопроцессор повреждений не имел, зато процессор был неработоспособен. Такие микросхемы превращали в «сопроцессор 80487». Но это уже из области экзотики, и, насколько мне известно, до России этот сопроцессор не дошёл.

Процессор Pentium во всех своих вариантах имел встроенный сопроцессор. Таким образом, с приходом этого процессора тип Real стал как бы обузой, а на передний план вышли Single, Double и Extended. Чтобы свести к минимуму необходимые переделки программ, Borland ввела новую директиву компилятора: <$REALCOMPATIBILITY ON/OFF>. По умолчанию стоит OFF, что означает отсутствие полной совместимости. В этом случае тип Real в Delphi совпадает с типом Double. Если же совместимость включена, тип Real совпадает со своим прообразом из Паскаля. Существует ещё тип Real48, который всегда, вне зависимости от настроек, совпадает со старым Real. Далее в этой статье под словом “Real” я всегда буду подразумевать старый тип. Отмечу, что всё это появилось только в Delphi 4, в более ранних версиях тип Real48 отсутствовал, а тип Real был всегда старым, шестибайтным.

Итак, теперь можно, наконец, добраться до размеров полей.

Тип Размер типа, байт Размер мантиссы, бит Размер экспоненты, бит
Single 4 23 8
Double 8 52 11
Extended 10 64 15
Real 6 40 7

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

Внутренний формат вещественных чисел

Рассмотрим тип Single, так как он является самым коротким и, следовательно, самым простым для понимания. Остальные типы отличаются от него только количественно. В дальнейшем числа в формате Single мы будем записывать как s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm, где s означает знаковый бит, e – бит экспоненты, m – бит мантиссы. Именно в таком порядке эти биты хранятся в четырёхбайтном значении (здесь учтена перестановка байтов; напоминаю, что в процессорах Intel байты в многобайтных значениях переставляются так, что младший байт идёт первым, а старший – последним). В мантиссе хранится двоичное число. Чтобы получить истинное значение мантиссы, к ней надо мысленно добавить слева единицу с точкой (то есть, например, мантисса 1010000000000000000000 означает двоичную дробь 1.101). Таким образом, имея 23 двоичных разряда, мы записываем числа с точностью до 24-ёх двоичных разрядов. Такая запись числа называется нормализованной.

Экспонента по определению всегда целое число. Но способ записи экспоненты в вещественных числах не совпадает с обычным способом записи чисел со знаком. Ноль в этом представлении записывается как 01111111. В обычном представлении это равно 127. Соответственно, 10000000 (128 в обычном представлении) означает единицу, а 01111110 (126) означает –1, и так далее (то есть из обычного беззнакового числа надо вычесть 127, и получится число, закодированное в экспоненте).


Из описанных выше правил есть исключения. Так, если все биты экспоненты равны нулю (то есть там стоит число –127), то к мантиссе перед её началом надо добавлять не “1.”, а “0.” (денормализованная запись). Это позволяет увеличить диапазон вещественных чисел. Если бы этого исключения не было бы, минимально возможное положительное число типа Single было бы равно примерно 5.9*10 -39 . А так появляется возможность использовать числа до 1.4*10 -45 . Побочным эффектом этого является то, что числа, меньшие, чем 1.17*10 -38 , представляются с меньшей, чем 24 двоичных разряда, точностью.

Если все биты в экспоненте равны единице, а в матрице – нулю, то мы получаем комбинацию, известную как INF (от английского Infinity – бесконечность). Эта комбинация используется тогда, когда результат вычислений превышает максимально допустимое форматом число. В зависимости от значения бита s бесконечность может быть положительной или отрицательной. Если же при такой экспоненте в мантиссе хоть один бит не равен нулю, такая комбинация называется NAN (Not A Number – не число). Попытки использования комбинаций NAN или INF приводят к ошибке времени выполнения.

Для задания нуля все биты мантиссы и экспоненты должны быть равны нулю (формально это означает 0*10 -127 ). С учётом описанных выше правил если хотя бы один бит экспоненты не будет равен нулю (т.е. экспонента будет больше -127), запись будет считаться нормализованной, и нулевая мантисса будет рассматриваться как единица. Поэтому никакие другие комбинации значений мантиссы и экспоненты не могут дать ноль.

Тип Double устроен точно так же, разница только в количестве разрядов и в том, какое значение экспоненты берётся за ноль. Итак, мы имеем 11 разрядов для экспоненты. За ноль берётся значение 1023.

Несколько иначе устроен Extended. Кроме количественных отличий добавляется ещё и одно качественное: в мантиссе явно указывается первый разряд. То есть, мантисса 1010. интерпретируется как 1.01, а не как 1.101, как это было в типах Single и Float. Поэтому если 23-битная мантисса типа Single обеспечивает 24-знаковую точность, а 52-битная мантисса Double – 53-битную, то 64-битная мантисса Extended обеспечивает 64-, а не 65-битную точность. Соответственно, при денормализованной форме записи первый разряд мантиссы явно содержит 0. За ноль экспоненты принимается значение 16383.

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

«Неполноценный» Extended

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

У сопроцессора есть специальный двухбайтный регистр, называемый управляющим словом. Установка отдельных битов этого регистра диктует сопроцессору то или иное поведение. Прежде всего, это связано с тем, какие исключения может возбуждать сопроцессор. Другие биты этого регистра отвечают за то, как будут округляться числа, как сопроцессор понимает бесконечность – всё это можно при необходимости узнать из документации Intel. Нас же будут интересовать только два бита из этого слова – восьмой и девятый. Именно они определяют, как будут обрабатываться числа внутри сопроцессора.

Если восьмой бит содержит единицу (так установлено по умолчанию), то десять байт внутренних регистров сопроцессора будут использоваться полностью, и мы получим «полноценный» Extended. Если же этот бит равен нулю, то всё определяется значением бита 9. Если он равен единице, то используются только 53 разряда мантиссы (остальные всегда равны нулю). Если же этот бит равен нулю – только 24 разряда мантиссы. Это увеличивает скорость вычислений, но уменьшает точность. Другими словами, точность работы сопроцессора может быть понижена до типа Double или даже Single. Но это касается только мантиссы, экспонента в любом случае будет содержать 15 бит, так что диапазон типа Extended сохраняется в любом случае.

Для работы с управляющим словом сопроцессора в модуле System описана переменная Default8087CW:Word и процедура Set8087CW(CW:Word). При запуске программы в переменную Default8087CW записывается то управляющее слово, которое установила система при запуске программы. Функция Set8087CW записывает новое значение в управляющее слово. Одновременно это новое значение записывается в переменную Default8087CW.

Такое поведение этой функции не всегда удобно – иногда бывает нужно сохранить старое значение переменной Default8087CW (впрочем, это несложно сделать, заведя дополнительную переменную). С другой стороны, если значение управляющего слова изменить, не используя Set8087CW (а в дальнейшем мы увидим, что такие изменения могут происходить помимо нашей воли), то с помощью функции Default8087CW просто нет возможности узнать текущее значение управляющего слова. В Delphi 6 и выше появилась функция Get8087CW, позволяющая узнать значение именно контрольного слова, а не переменной Default8087CW. В более ранних версиях единственный способ получить значение этого слова – использование ассемблера, тем более что в Delphi нет проблем с ассемблерными вставками.

Установить значение управляющего слова можно с помощью команды FLDCW, прочитать – с помощью FNSTCW. Обе эти команды имеют один аргумент – переменную типа Word. Чтобы, например, установить 53-значную точность, не изменив при этом другие биты управляющего слова, надо выполнить такую последовательность команд:

Современные сопроцессоры обрабатывают числа с такой скоростью, что при обычных вычислениях вряд ли может возникнуть необходимость в ускорении за счёт точности — выигрыш будет ничтожен. Эта возможность используется, в основном, в тех случаях, когда вычисления с плавающей точкой составляют значительную часть программы, а высокая точность не имеет принципиального значения (например, в движках для 3D-игр). Однако забывать об этой особенности работы сопроцессора не стоит, потому что она может преподнести один неприятный сюрприз, о котором чуть позже.

Бесконечные дроби

Из школы мы все помним, что не каждое число может быть записано конечной десятичной дробью. Бесконечные же дроби бывают двух видов: периодичные и непериодичные. Примером непериодичной дроби является число «пи», периодичной – число 1/3 или любая другая простая дробь, не представимая в виде конечной десятичной дроби.

Для тех, кто забыл математику, напомню, что периодичные дроби – это такие дроби, которые содержат бесконечно повторяющуюся последовательность цифр. Например, 1/9=0.11111…, 1/12=0.08333333…, 1/7=0.142857142857… Для записи таких чисел используют скобки – в них заключают повторяющуюся часть. Те же числа должны быть записаны так: 1/9=0.1(1), 1/12=0.08(3), 1/7=0.1(428571).

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

До сих пор мы говорили о только о десятичных бесконечных дробях. Но двоичные дроби тоже могут быть бесконечными. Даже более того, любое число, выражаемое конечной двоичной дробью, может быть также выражено и десятичной конечной дробью. Но существуют числа (например, 1/5) которые выражаются конечной десятичной дробью, но не могут быть выражены конечной двоичной дробью. Это сильно усложняет жизнь программистам.

Примеры «неправильного» поведения вещественных типов

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

Все примеры построены одинаково: на форму надо кинуть два компонента – метку (TLabel) и кнопку (TButton). Так как это только примеры, я не стал придумывать имена для этих компонентов, пусть называются Button1 и Label1. Обработчик Button1Click содержит некоторый код, результаты работы которого выводятся на форму через Label1. Таким образом, нужно запустить программу, нажать на кнопку и посмотреть, что будет написано в метке. Я буду приводить только код обработчика Button1Click, так как всё остальное тривиально. Напомню, что в Паскале допускается не ставить точку с запятой перед end’ом (за исключением end’а в описании класса) и перед until’ом. Я предпочитаю пользоваться этой возможностью, так что не надо тыкать в меня пальцем, что я забываю ставить точки с запятой.

Пример первый – «неправильное значение»

Пример второй – сравнение

При нажатии кнопки мы увидим надпись «Не равно». На первый взгляд это кажется абсурдом. Действительно, мы уже знаем, что переменная R получает значение 0.100000001490116 вместо 0.1. Но ведь «0.1» в правой части равенства тоже должно преобразоваться по тем же законам, ведь в компьютере всё предопределено. Тут самое время вспомнить, что процессоры Intel работают только с 10-байтным типом Extended, поэтому и левая, и правая часть равенства сначала преобразуется в этот тип, и лишь потом производится сравнение. То корявое число, которое оказалось в переменной R вместо 0.1, хоть и выглядит страшно, но зато представляется в виде конечной двоичной дроби. Информация же о том, что это на самом деле должно означать «0.1», нигде не сохранилось. При преобразовании этого числа в Extended младшие, избыточные по сравнению с типом Single разряды мантиссы просто заполняются нулями, и мы снова получим то же самое число, только записанное в формате Extended. А «0.1» из правой части равенства преобразуется в Extended без промежуточного превращения в Single. А 0.1 – бесконечная в двоичном представлении дробь. Поэтому некоторые из младших разрядов мантиссы будут содержать единицы. Другими словами, мы получим хоть и не точное представление числа 0.1, но всё же более близкое к истине, чем 0.100000001490116. Из-за таких хитрых преобразований оказывается, что мы сравниваем два близких, но всё же не равных числа. Отсюда – закономерный результат в виде надписи «Не равно».

Тут уместна аналогия с десятичными дробями. Допустим, в одном случае мы делим 1 на три с точностью до трёх знаков, и получаем 0.333. Потом мы делим 1 на три с точностью то четырёх знаков, и получаем 0.3333. Теперь мы хотим сравнить эти два числа. Для этого приводим их к точности в четыре разряда. Получается, что мы сравниваем 0.3330 и 0.3333. Очевидно, что это разные числа.

Если попробовать заменить число 0.1 на 0.5, то мы получим «Равно». Думаю, вы уже знаете почему, но для полноты текста объясню. 0.5 – это конечная двоичная дробь. При прямом приведении её к типу Extended в младших разрядах оказываются нули. Точно такие же нули оказываются в этих разрядах при превращении числа 0.5 типа Single в тип Extended. Поэтому в результате мы сравниваем два числа. Это похоже, как если бы мы делили 1 на 4 с точностью до трёх и до четырёх значащих цифр. В первом случае получили бы 0.250, во втором – 0.2500. Приведя их оба к точности в четыре знака, получим сравнение 0.2500 и 0.2500. Очевидно, что эти цифры равны.

Пример третий – сравнение разных типов

Наученные горьким опытом, вы, наверное, ожидаете увидеть надпись «Не равно». Что ж, жизнь вас не разочарует, именно это вы и увидите. Тип Double точнее, чем Single (хотя его точности тоже не хватает для представления бесконечной дроби). В R2 мы получим не 0.100000001490116, а другое число, с точностью 15-16 десятичных знаков. Я не могу назвать точно это число, потому что FloatToStr воспринимает его как 0.1, так что, заменив в первом примере Single на Double, вы увидите 0.1 (только не надо обольщаться, всё равно это не 0.1, просто функция FloatToStr имеет такую особенность работы). Числа в обеих переменных приводятся к типу Extended, но при этом они не меняются и, как были не равны, так и остаются неравными. Это напоминает ситуацию, когда мы сравниваем 0.333 и 0.3333, приводя их к точности в пять знаков: числа 0.33300 и 0.33330 не равны.

Мне уже неловко надоедать вам такими очевидными замечаниями, но всё-таки: если в этом примере заменить 0.1 на 0.5, мы увидим «Равно».

Пример четвёртый – вычитание в цикле

Пример пятый – сюрпириз от Microsoft

Изменим в предыдущем примере тип переменной R с Single на Double. Значение, выводимое программой, станет 1.44327637948555E-16. Вполне логичный и предсказуемый результат, так как тип Double точнее, чем Single и, следовательно, все вычисления более точны, мы просто обязаны получить более точный результат. Хотя, разумеется, абсолютная точность (то есть ноль), для нас остаётся недостижимым идеалом.

А теперь – вопрос на засыпку. Изменится ли результат, если мы заменим Double на более точный Extended? Ответ не такой однозначный, каким его хотелось бы видеть. В принципе, после такой замены вы должны получить -6.7762635780344E-20. Но в некоторых случаях от замены Double на Extended результат не изменится, и вы снова получите 1.44327637948555E-16. Это зависит от операционной системы.


Всё дело в использовании «неполноценного» Extended. При запуске программы любая система устанавливает такое управляющее слово сопроцессора, чтобы Extended был полноценным. Но затем программа вызывает много разных функций Windows API. Какая-то (или какие-то) из этих многочисленных функций в некорректно работают с управляющим словом, меняя его значение и не восстанавливая при выходе. Такая проблема встречается, в основном, в Windows 95 и старых версиях Windows 98. Также имеются сведения о том, что управляющее слово может портиться и в Windows NT, причём эффект наблюдался не сразу после установки системы, а лишь через некоторое время, после доустановки других программ. Проблема именно в некорректности поведения системных функций; значение управляющего слова, устанавливаемое системой при запуске программы, всегда одинаково. Эта проблема известна: например, в исходных кодах VCL можно найти сохранение управляющего слова сопроцессра перед вызовом некоторых API-функций с последующим его восстановлением. Комментарии сообщают, что функция может изменить значение управляющего слова, поэтому необходимо его сохранение и восстановление.

Таким образом, приходим к неутешительному выводу: к тем проблемам с вещественными числами, которые обусловлены особенностями их аппаратной реализации, добавляются ещё и баги Windows. Правда, радует то, что в последнее время эти баги встречаются крайне редко — видимо, новые версии системы ведут себя более ответственно. Тем не менее, полностью исключать такую возможность нельзя, особенно если ваша программа будет использоваться на устаревшей технике с устаревшими системами (например, в образовательных учреждениях, финансирование которых оставляет желать лучшего). Чтобы наш пример всегда выдавал правильное значение -6.7762635780344E-20, достаточно поставить в начале нашей процедуры Set8087CW(Get8087CW or $0100), и программа в любой системе будет использовать сопроцессор в режиме максимальной точности. (Если вы используете старые версии Delphi, эту строку можно заменить на Set8087CW(Default8087CW), если, конечно, значения по умолчанию прочих флагов управляющего слова вас устраивают.)

Раз уж мы заговорили об управляющем слове, давайте немного поэкспериментируем с ним. Изменим первую строчку на Set8087CW(Get8087CW and $FCFF or $0200). Тем самым мы переведём сопроцессор в режим 53-ёхразрядной точности представления мантиссы. Теперь в любой системе мы увидим 1.44327637948555E-16, несмотря на использование Extended. Если же мы изменим первую строчку на Set8087CW(Get8087CW and $FCFF), то будем работать в режиме 24-ёхразрядной точности. Соответственно, в любой системе будет результат -7.3015691270939E-8.

Заметим, что при загрузке в 10-байтный регистр сопроцессора числа типа Extended в режиме пониженной точности «лишние» биты не обнуляются. Только результаты математических операций представляются с пониженной точностью. Кроме того, при сравнении двух чисел также учитываются все биты, независимо от точности. Поэтому код

при выборе любой точности даст «Не равно».

Пример шестой – машинное эпсилон

Когда мы имеем дело с вычислениями с ограниченной точностью, возникает такой парадокс. Пусть, например, мы считаем с точностью до трёх значащих цифр. Прибавим к числу 1.00 число 1.00*10 -4 . Если бы всё было честно, мы получили бы 1.0001. Но у нас ограничена точность, поэтому мы вынуждены округлять до трёх значащих цифр. В результате получается 1.00. Другими словами, мы прибавляем к единице некоторое число, большее нуля, а в результате из-за ограниченной точности получаем снова единицу. Наименьшее положительное число, которое при добавлении его к единице даёт результат, не равный единице, называется машинным эпсилон.

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

Прежде чем искать машинное эпсилон программно, попытаемся найти его из теоретических соображений. Итак, мантисса типа Extended содержит 64 разряда. Чтобы закодировать единицу, старший бит мантиссы должен быть равен 1 (денормализованная запись), остальные биты — нулю. Очевидно, что при такой записи наименьшее из чисел, для которых вполняется условие x>1, получается, когда самый младший бит мантиссы тоже будет равен единице, т.е. x=1.00. 001 (в двоичном представлении; между точкой и младшей единицей 62 нуля). Таким образом, машинное эпсилон равно x-1, т.е. 0.00. 001. В более привычной десятичной форме записи это будет 2 -63 , т.е. примерно 1.084*10 -19 .

Теперь напишем программу для отыскания машинного эпсилон.

В результате на экране появится число 1.0842021724855E-19 в полном соответствии с теоретическими выкладками (если в вашей системе присутствует описанный выше баг с переводом процессора в режим пониженной точности, вместо этого числа вы получите 2.22044604925031E-16, т.е. 2 -52 . Чтобы этого не происходило, исправьте значение управляющего слова).

А теперь заменим тип Extended на Double. Результат не изменится. На Single – опять не изменится. Но такое поведение лишь на первый взгляд может показаться странным. Давайте подробнее рассмотрим выражение 1+R/2>1. Итак, все вычисления (в том числе и сравнение) сопроцессор выполняет с данными типа Extended. Последовательность действий такова: число R загружается в регистр сопроцессора, преобразуясь при этом к типу Extended. Дальше оно делится на 2, а затем к результату прибавляется 1, и всё это в Extended, никакого обратного преобразования в Single или Double не происходит. Затем это число сравнивается с единицей. Очевидно, что результат сравнения не должен зависеть от исходного типа R.

Заключение

В этой статье я постарался объяснить внутреннее устройство вещественных чисел с точки зрения процессоров Intel и упомянуть некоторые проблемы, которые с ними связаны. На самом деле все проблемы сводятся к двум: во-первых, не всякое вещественное число может быть представлено точно, и, во-вторых, не всякое вещественное число, представимое в виде конечной десятичной дроби, представимо в виде конечной двоичной дроби. Вторая проблема, наверное, приносит больше неприятностей начинающим пользователям, так как она менее очевидна. Рецепты преодоления этих проблем я сознательно не излагаю, так как оптимальный вариант очень сильно зависит от конкретной задачи. Человеку же, понявшему причины появления проблем, не составит труда в каждом конкретном случае подобрать наиболее приемлемое решение. В этом, собственно, и заключается разница между программистом и ламером: первый разбирается в задаче и находит для неё решение, второй умеет только кидать на форму готовые компоненты и передирать куски чужого кода. А эту статью я писал для начинающих программистов, а не для начинающих ламеров, отсюда и такой стиль.

Огромное спасибо Елене Филипповой за помощь в поиске информации.

Extended — Тип Delphi

Столкнулся с тем, что в некоем решении правых частей дифференциальных уравнений принципиально использование double или Extended. Вопрос глуп, но все же — как определить требуемую точность решения этих самых уравнений?

Заранее сообщаю, что я совсем не математик.

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

[1] Vaitek © (04.01.05 23:28)

Вообще то я сообщил, что не математик. А точность решения определяется неким числом, которое как в double, так и в Extended одинаково пишется.

Extended позволяет тебе делать вычисления с точностью до 19-20 знаков, double — только 15-16. Отсюда и исходи

[3] DrPass © (04.01.05 23:56)

Ну покедова я в справку умею лазить. Хотя спасибо за совет..


> olookin © (05.01.05 01:11) [4]
> Ну покедова я в справку умею лазить.

Если хочешь нормальный ответ, то задай нормальный вопрос, а то пока вся эта ветка тянет на «орех».


> как определить требуемую точность решения этих самых уравнений

Тебе уже ответил Vaitek © (04.01.05 23:28) что точность решения определяется задачей. И уже исходя из заданной точности результата нужно выбирать точность промежуточных вычислений по количеству значащих цифр. Чтобы сделать это не нужно быть математиком, нужно быть программистом.

[5] Almaz © (05.01.05 01:25)

>>Если хочешь нормальный ответ, то задай нормальный вопрос, а то пока вся эта ветка тянет на «орех».

Логично, но вот нормального вопроса задать то как раз и не могу. За что и прошу прощения.

>>Тебе уже ответил Vaitek © (04.01.05 23:28) что точность решения определяется задачей. И уже исходя из заданной точности результата нужно выбирать точность промежуточных вычислений по количеству значащих цифр. Чтобы сделать это не нужно быть математиком, нужно быть программистом.

Не будучи математиком, определить требуемую точность per se не могу.

Как бы это выразиться. Есть у меня некая структура, работающая с нечто. До сб пору это нечто выдавало и принимало число в формате double. И вот выросло чудо, которое уже просит не double, а Extended. В противном случае не работает или работает неправильно (в зависимости от условий). Ну как я еще вопрос могу сформулировать.

На самом деле автор вопроса (я т.е.) уже сильно поддамши, поэтому может даже и не стоит отвечать.

Ну, во-первых выспись, во-вторых, уточни, что конкретно делает твоя прога.

[8] Vaitek © (05.01.05 09:10)


А прога, прога тут как-бы ни причем. Вообще говоря, тут дело а) в методе решения этих дифуров и б) скорости решения одного шага (или такта или как там это называется) этих уравнений. Т.е. либо Рунге-Кутт и медленно, но можно double, либо Эйлер и быстро, но уже Extended. Либо несколько раз Эйлера с меньшим шагом и тогда чуть быстрее и уже можно double использовать. В общем, варьирую и смотрю.

Delphi: несовместимые типы: ‘integer’ и ‘extended’

Мне нужно составить программу, которая определит сумму, которую вы будете получать за отработанные часы. Вот код:

Сообщение об ошибке:

Пожалуйста, обратите внимание: я новичок в программировании, и это домашнее задание по ИТ Любая помощь приветствуется! Заранее спасибо!

1 ответ

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

Например, вы можете округлить до ближайшего:

Или вы можете использовать Floor , чтобы получить наибольшее целое число, меньшее или равное значению с плавающей запятой:

Или, возможно, Ceil , наименьшее целое число больше или равно значению с плавающей запятой:

Для получения более общего совета я советую вам попробовать заглянуть в документацию, если вы столкнулись с ошибкой, которую не понимаете. Каждая ошибка компилятора документирована. Вот документация для несовместимых типов E2010: http://docwiki.embarcadero.com/RADStudio/en/E2010_Incompatible_types_-_%27%25s%27_and_%27%25s%27_%28Delphi%29

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

Типы данных Delphi и работа с ними

К встроенным типам данных в языке Delphi относятся типы целые, действительные, символы, строки, указатели, булевы.

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

Структурные типы. К структурным типам относятся множества, массивы, записи, файлы, классы, интерфейсы.

Целые типы данных. В переменных целых типов информация представляется в виде целых чисел, т.е. чисел не имеющих дробной части.

Таблица 1 Операции над порядковыми типами

Минимальное значение порядкового типа Т

Максимальное значение порядкового типа Т

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

Предыдущее по порядку значение. Для целых выражений эквивалентно Х-1

Следующее по порядку значение. Для целых выражений эквивалентно Х+1

Уменьшает значение переменной на 1. Эквивалентно V := Pred(V)

Увеличивает значение переменной на 1. Эквивалентно V := Succ(V)

8 битов, беззнаковый

16 битов, беззнаковый

32 бита, беззнаковый

Также существует такой тип, как Integer, который эквивалентен типу LongInt. Его диапазон от -2147483648 до 21474836478. Занимает 4 байта в пямяти. Основными являются Integer и Cardinal, так что в большинстве случаев желательно использовать эти типы.

Над целыми данными выполняются все операции, определенные для порядковых типов. Операции над целыми типами:

Возвращает абсолютное целое значение Х

Возвращает целую часть частного деления Х на Y

Возвращает остаток частного деления Х на Y

Возвращает булево True (истина), если Х — нечетное целое, и False (ложь) — в противном случае


Действительные типы данных. В переменных действительных типов содержатся числа, состоящие из целой и дробной частей.

Количество значащих цифр

Основным, обеспечивающим максимальную производительность, является тип Real, который в настоящий момент эквивалентен типу Double.

Таблица 5 Функции действительных типов

Абсолютная величина х

Косинус х (х выражается в радианах, а не в градусах)

Экспоненциальная функция от х

Дробная часть х

Целая часть х. Несмотря на название, возвращает действительное значение (с плавающей запятой), т.е. просто устанавливает нуль в дробной части

Натуральный логарифм от х

Ближайшее к х целое значение. Возвращает значение целого типа. Условие «ближайшее к х» не работает, если верхнее и нижнее значения оказываются равноудаленными (например, если дробная часть точно равна 0,5). В этих случаях Delphi перекладывает решение на операционную систему. Обычно процессоры Intel решают эту задачу в соответствии с рекомендацией IEEE округлять в сторону ближайшего четного целого числа. Иногда такой подход называют «банкирским округлением»

Квадрат х, т.е. X*X

Квадратный корень от х

Целая часть х. В отличие от Int, возвращающей

Символьные типы данных. Символьные типы предназначены для хранения одного символа.

Однобайтовые символы, упорядоченные в соответствии с расширенным набором символов ANSI

Символы объемом в слово, упорядоченные в соответствии с международным набором символов UNICODE. Первые 256 символов совпадают с символами ANSI

Булевы типы данных. Переменные булевых типов данных представляют логические значения, например, true (истина) и false (ложь).

Таблица 7 Размеры переменных булевых типов

2 байт (объем Word)

4 байт (объем Longint)

Массив — это структура данных, представляющая собой набор переменных одинакового типа, имеющих общее имя. Массивы удобно использовать для хранения однородной по своей природе информации, например, таблиц и списков.

Массив, как и любая переменная программы, перед использованием должен быть объявлен в разделе объявления переменных. В общем виде инструкция объявления массива выгладит следующим образом:

Имя: [нижний_индекс..верхний_индекс] of тип

где: имя — имя массива;

array — зарезервированное слово языка Delphi, обозначающее, что объявляемое имя является именем массива;

нижний_индекс и верхний_индекс — целые константы, определяющие диапазон изменения индекса элементов массива и, неявно, количество элементов (размер) массива;

DelphiComponent.ru — бесплатно видеоуроки по Delphi, статьи, исходники

Вещественные типы (Действительные типы) в Delphi XE

Значение переменных вещественного типа всегда приблизительные, по­этому их нельзя проверять на равенство!

-2.9×10 -39 ..1.7×10 38

-1.5×10 -45 ..3.4×10 38

-5.0×10 -324 ..1.7×10 3U8

-3.6×10 -4951 «1.1×10 4y32

-2×10 63 +1 ..2×10 63 -1


Тип Real 48 оставлен только для совместимости с предыдущими версиями Delphi , поэтому его не следует использовать.

Типы Comp и Currency используются только в денежных операциях.

Обычно для расчётов хватает точности типа Single , но вычисления произ­водятся быстрее с типом Real (он же Double ).

Для записи вещественных чисел используют 2 способа. Первый — как в школе, но вместо десятичной запятой ставится точка:

Для очень больших и очень маленьких чисел больше подходит второй способ. Сначала записывается однозначное число с десятичной точкой, за­тем буква E (или е) и целое число, которое равно степени десятки. Напри­мер,

Объявление переменных вещественного типа:

Вещественные числа часто используются для «технических» расчётов, а при необходимости их можно округлить до целых чисел (тип результата — int 64) с помощью функций trunc и round .

Целым переменным нельзя присваивать действительные значения!

Функция trunc просто отсекает дробную часть числа:

Функция round округляет действительное число до ближайшего целого:

Чтобы было легче понять, как действует функция round , выразим её через trunc :

Заметка о RTTI, TValue и real/double/extended

Заканчивая свою работу над сериализацией записей в XML, и уже встраивая разработанные классы в программный продукт, столкнулся с неожиданным поведением, что выражалось некорректной загрузкой данных. Сначала было сложно понять в чем дело, ибо на тестовом примере все вроде как работало исправно.

Returns the number of bytes occupied by the stored value. DataSize returns the number of bytes occupied by the stored value. For simple types, DataSize is equal to the number of bytes that SizeOf returns.

Комментарии

Добрый день! С интересом прочитал статью, спасибо. Хотелось бы только немного защитить Delphi ;) Защитить от фразы: «столкнулся с неожиданным поведением».
Что касается того кода, из-за которого, собственно, случился информационный повод. Смотрим описание StrToFloat:

function StrToFloat ( FloatString : string ) : Extended;

Название функции зело обманчиво, наверное, StrToExtended было бы самое оно. Но. имеем то, что имеем.
Но это не главное в данном случае. Главное кроется за тем кодом, который Вы использовали для проверок: прямое присвоение.
Обратимся к первоисточнику (System.Rtti.pas). А там:
.
class operator Implicit(const Value: string): TValue;
class operator Implicit(Value: Integer): TValue;
class operator Implicit(Value: Extended): TValue;
class operator Implicit(Value: Int64): TValue;
class operator Implicit(Value: TObject): TValue;
class operator Implicit(Value: TClass): TValue;
class operator Implicit(Value: Boolean): TValue;
.
То есть, неявного присвоения для Double не предусмотрено, предусмотрено для Extended. Таким образом, для прямого присвоения сначала делается неявное преобразование Double в Extended, а этот Extended затем и присваивается TValue. Собственно, все происходит так, как и должно происходить. Поэтому фразу, на мой взгляд, следует переделать на: «столкнулся с ожидаемым поведением» :)

хех (: действительно, вы правы про StrToFloat, че то мне казалось что он double возвращает всегда (:
Спасибо за комментарий, век живи — век учись, как говорится (:

Сам видел, что в TValue нет свойства AsFloat но что то не придал этому значения (:

Но в итоге для хранения real/double в TValue все равно от неявной записи следует отказаться, иначе будет все равно extended, так что через TValue.Make.

Абсолютно с Вами согласен, хочу только дополнить, что есть немного более удобная, на мой взгляд, функция, тоже, кстати, функция класса TValue:

class function From(const Value: T): TValue; static;

Конечно, внутри себя она использует тот же самый Make.

Прошу прощения, движок ожидаемо «съел» все, что в угловых скобках. попробую так, может, получится:

class function From (const Value: T): TValue; static;

Delphi in a Nutshell by Ray Lischner

Stay ahead with the world’s most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to v > Start Free Trial

No credit card required

Syntax

Description

The Extended type is an Intel standard floating-point type that uses 10 bytes to store a sign bit, a 15-bit exponent, and a 64-bit mantissa. Extended conforms to the minimum requirements of the IEEE-754 extended double precision type.

The limits of the Extended type are approximately 3.37 × 10 -4932 to 1.18 × 10 4932 , with about 19 decimal digits of precision.

Unlike Single and Double , Extended contains all of its significant bits. Normalized values, infinity, and not-a-number have an explicit 1 bit as the most significant bit. Table 5-2 shows the detailed format of finite and special Extended values. Not all bit patterns are val >Extended values. Delphi raises runtime error 6 ( EInvalidOp ) if you try to use an invalid bit pattern as a floating-point number.

Илон Маск рекомендует:  Что такое код ldap_next_entry
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL